1use std::sync::Arc;
2
3use anyhow::Context;
4use async_trait::async_trait;
5use committable::{Commitment, Committable};
6use espresso_types::{
7 BackoffParams, BlockMerkleTree, FeeAccount, FeeAccountProof, FeeMerkleCommitment, Leaf2,
8 NodeState, PubKey, SeqTypes,
9 traits::{SequencerPersistence, StateCatchup},
10 v0_3::{ChainConfig, RewardAccountProofV1, RewardAccountV1, RewardMerkleCommitmentV1},
11 v0_4::{
12 PermittedRewardMerkleTreeV2, RewardAccountV2, RewardMerkleCommitmentV2,
13 forgotten_accounts_include,
14 },
15};
16use hotshot::traits::NodeImplementation;
17use hotshot_new_protocol::{storage::NewProtocolStorage, utils::verify_new_protocol_leaf_chain};
18use hotshot_types::{
19 data::ViewNumber, epoch_membership::EpochMembershipCoordinator, message::UpgradeLock,
20 simple_certificate::LightClientStateUpdateCertificateV2, traits::network::ConnectedNetwork,
21};
22use jf_merkle_tree_compat::{ForgetableMerkleTreeScheme, MerkleTreeScheme};
23use request_response::RequestType;
24use tokio::time::timeout;
25use versions::NEW_PROTOCOL_VERSION;
26
27use crate::{
28 api::RewardMerkleTreeV2Data,
29 catchup::verify_legacy_leaf_chain,
30 request_response::{
31 RequestResponseProtocol,
32 request::{Request, Response},
33 },
34};
35
36#[async_trait]
37impl<I: NodeImplementation<SeqTypes>, N: ConnectedNetwork<PubKey>, P: SequencerPersistence>
38 StateCatchup for RequestResponseProtocol<I, N, P>
39where
40 I::Storage: NewProtocolStorage<SeqTypes>,
41{
42 async fn try_fetch_leaf(
43 &self,
44 _retry: usize,
45 coordinator: EpochMembershipCoordinator<SeqTypes>,
46 height: u64,
47 ) -> anyhow::Result<Leaf2> {
48 let timeout_duration = self.config.request_batch_interval * 3;
50
51 timeout(timeout_duration, self.fetch_leaf(coordinator, height))
53 .await
54 .with_context(|| "timed out while fetching leaf")?
55 }
56
57 async fn try_fetch_accounts(
58 &self,
59 _retry: usize,
60 instance: &NodeState,
61 height: u64,
62 view: ViewNumber,
63 fee_merkle_tree_root: FeeMerkleCommitment,
64 accounts: &[FeeAccount],
65 ) -> anyhow::Result<Vec<FeeAccountProof>> {
66 let timeout_duration = self.config.request_batch_interval * 3;
68
69 timeout(
71 timeout_duration,
72 self.fetch_accounts(
73 instance,
74 height,
75 view,
76 fee_merkle_tree_root,
77 accounts.to_vec(),
78 ),
79 )
80 .await
81 .with_context(|| "timed out while fetching accounts")?
82 }
83
84 async fn try_remember_blocks_merkle_tree(
85 &self,
86 _retry: usize,
87 instance: &NodeState,
88 height: u64,
89 view: ViewNumber,
90 mt: &mut BlockMerkleTree,
91 ) -> anyhow::Result<()> {
92 let timeout_duration = self.config.request_batch_interval * 3;
94
95 timeout(
97 timeout_duration,
98 self.remember_blocks_merkle_tree(instance, height, view, mt),
99 )
100 .await
101 .with_context(|| "timed out while remembering blocks merkle tree")?
102 }
103
104 async fn try_fetch_chain_config(
105 &self,
106 _retry: usize,
107 commitment: Commitment<ChainConfig>,
108 ) -> anyhow::Result<ChainConfig> {
109 let timeout_duration = self.config.request_batch_interval * 3;
111
112 timeout(timeout_duration, self.fetch_chain_config(commitment))
114 .await
115 .with_context(|| "timed out while fetching chain config")?
116 }
117
118 async fn try_fetch_reward_merkle_tree_v2(
119 &self,
120 _retry: usize,
121 height: u64,
122 view: ViewNumber,
123 reward_merkle_tree_root: RewardMerkleCommitmentV2,
124 accounts: Arc<Vec<RewardAccountV2>>,
125 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
126 let timeout_duration = self.config.request_batch_interval * 3;
128
129 timeout(
131 timeout_duration,
132 self.fetch_reward_merkle_tree_v2(height, view, reward_merkle_tree_root, accounts),
133 )
134 .await
135 .with_context(|| "timed out while fetching reward merkle tree v2")?
136 }
137
138 async fn try_fetch_reward_accounts_v1(
139 &self,
140 _retry: usize,
141 instance: &NodeState,
142 height: u64,
143 view: ViewNumber,
144 reward_merkle_tree_root: RewardMerkleCommitmentV1,
145 accounts: &[RewardAccountV1],
146 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
147 let timeout_duration = self.config.request_batch_interval * 3;
149
150 timeout(
152 timeout_duration,
153 self.fetch_reward_accounts_v1(
154 instance,
155 height,
156 view,
157 reward_merkle_tree_root,
158 accounts.to_vec(),
159 ),
160 )
161 .await
162 .with_context(|| "timed out while fetching reward accounts")?
163 }
164
165 async fn try_fetch_state_cert(
166 &self,
167 _retry: usize,
168 epoch: u64,
169 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
170 let timeout_duration = self.config.request_batch_interval * 3;
171
172 timeout(timeout_duration, self.fetch_state_cert(epoch))
173 .await
174 .with_context(|| "timed out while fetching state cert")?
175 }
176
177 fn backoff(&self) -> &BackoffParams {
178 unreachable!()
179 }
180
181 fn name(&self) -> String {
182 "request-response".to_string()
183 }
184
185 async fn fetch_accounts(
186 &self,
187 _instance: &NodeState,
188 height: u64,
189 view: ViewNumber,
190 fee_merkle_tree_root: FeeMerkleCommitment,
191 accounts: Vec<FeeAccount>,
192 ) -> anyhow::Result<Vec<FeeAccountProof>> {
193 tracing::info!("Fetching accounts for height: {height}, view: {view}");
194
195 let accounts_clone = accounts.clone();
197 let response_validation_fn = move |_request: &Request, response: Response| {
198 let accounts_clone = accounts_clone.clone();
200
201 async move {
202 let Response::Accounts(fee_merkle_tree) = response else {
204 return Err(anyhow::anyhow!("expected accounts response"));
205 };
206
207 let mut proofs = Vec::new();
209 for account in accounts_clone {
210 let (proof, _) = FeeAccountProof::prove(&fee_merkle_tree, account.into())
211 .with_context(|| format!("response was missing account {account}"))?;
212 proof.verify(&fee_merkle_tree_root).with_context(|| {
213 format!(
214 "invalid proof for fee account {account}, root: {fee_merkle_tree_root}"
215 )
216 })?;
217 proofs.push(proof);
218 }
219
220 Ok(proofs)
221 }
222 };
223
224 let response = self
226 .request_indefinitely(
227 Request::Accounts(height, *view, accounts),
228 RequestType::Batched,
229 response_validation_fn,
230 )
231 .await
232 .with_context(|| "failed to request accounts")?;
233
234 tracing::info!("Fetched accounts for height: {height}, view: {view}");
235
236 Ok(response)
237 }
238
239 async fn fetch_leaf(
240 &self,
241 coordinator: EpochMembershipCoordinator<SeqTypes>,
242 height: u64,
243 ) -> anyhow::Result<Leaf2> {
244 tracing::info!("Fetching leaf for height: {height}");
245
246 let leaf_chain = self
249 .request_indefinitely(
250 Request::Leaf(height),
251 RequestType::Batched,
252 move |_request: &Request, response: Response| async move {
253 let Response::Leaf(leaves) = response else {
254 return Err(anyhow::anyhow!("expected leaf response"));
255 };
256 if leaves.is_empty() {
257 return Err(anyhow::anyhow!(
258 "received empty leaf chain for height {height}"
259 ));
260 }
261 Ok(leaves)
262 },
263 )
264 .await
265 .with_context(|| "failed to request leaf chain")?;
266
267 let result = if leaf_chain[0].block_header().version() >= NEW_PROTOCOL_VERSION {
268 let upgrade_lock =
270 UpgradeLock::<SeqTypes>::new(versions::Upgrade::trivial(NEW_PROTOCOL_VERSION));
271 let cert2_height = leaf_chain
274 .last()
275 .ok_or_else(|| anyhow::anyhow!("empty leaf chain for height {height}"))?
276 .height();
277 let cert2 = self
278 .request_indefinitely(
279 Request::Cert2(cert2_height),
280 RequestType::Batched,
281 move |_request: &Request, response: Response| async move {
282 let Response::Cert2(cert2) = response else {
283 return Err(anyhow::anyhow!("expected cert2 response"));
284 };
285 if cert2.data.block_number != cert2_height {
286 return Err(anyhow::anyhow!(
287 "received cert2 at height {} but expected exactly {cert2_height}",
288 cert2.data.block_number
289 ));
290 }
291 Ok(cert2)
292 },
293 )
294 .await
295 .with_context(|| format!("failed to request cert2 at height {cert2_height}"))?;
296
297 verify_new_protocol_leaf_chain(leaf_chain, &coordinator, height, &upgrade_lock, cert2)
298 .await
299 .with_context(|| "leaf chain verification with cert2 failed")?
300 } else {
301 verify_legacy_leaf_chain(leaf_chain, &coordinator, height).await?
302 };
303
304 tracing::info!("Fetched leaf for height: {height}");
305 Ok(result)
306 }
307
308 async fn fetch_chain_config(
309 &self,
310 commitment: Commitment<ChainConfig>,
311 ) -> anyhow::Result<ChainConfig> {
312 tracing::info!("Fetching chain config with commitment: {commitment}");
313
314 let response_validation_fn = move |_request: &Request, response: Response| {
316 async move {
317 let Response::ChainConfig(chain_config) = response else {
319 return Err(anyhow::anyhow!("expected chain config response"));
320 };
321
322 if commitment != chain_config.commit() {
324 return Err(anyhow::anyhow!("chain config commitment mismatch"));
325 }
326
327 Ok(chain_config)
328 }
329 };
330
331 let response = self
333 .request_indefinitely(
334 Request::ChainConfig(commitment),
335 RequestType::Batched,
336 response_validation_fn,
337 )
338 .await
339 .with_context(|| "failed to request chain config")?;
340
341 tracing::info!("Fetched chain config with commitment: {commitment}");
342
343 Ok(response)
344 }
345
346 async fn remember_blocks_merkle_tree(
347 &self,
348 _instance: &NodeState,
349 height: u64,
350 view: ViewNumber,
351 mt: &mut BlockMerkleTree,
352 ) -> anyhow::Result<()> {
353 tracing::info!("Fetching blocks frontier for height: {height}, view: {view}");
354
355 let mt_clone = mt.clone();
357
358 let response_validation_fn = move |_request: &Request, response: Response| {
360 let mut block_merkle_tree = mt_clone.clone();
362
363 async move {
364 let Response::BlocksFrontier(blocks_frontier) = response else {
366 return Err(anyhow::anyhow!("expected blocks frontier response"));
367 };
368
369 let leaf_elem = blocks_frontier
371 .elem()
372 .with_context(|| "provided frontier is missing leaf element")?;
373
374 block_merkle_tree
376 .remember(
377 block_merkle_tree.num_leaves() - 1,
378 *leaf_elem,
379 blocks_frontier,
380 )
381 .with_context(|| "merkle tree verification failed")?;
382
383 Ok(block_merkle_tree)
385 }
386 };
387
388 let response = self
390 .request_indefinitely(
391 Request::BlocksFrontier(height, *view),
392 RequestType::Batched,
393 response_validation_fn,
394 )
395 .await
396 .with_context(|| "failed to request blocks frontier")?;
397
398 *mt = response;
400
401 tracing::info!("Fetched blocks frontier for height: {height}, view: {view}");
402
403 Ok(())
404 }
405
406 async fn fetch_reward_merkle_tree_v2(
407 &self,
408 height: u64,
409 view: ViewNumber,
410 reward_merkle_tree_root: RewardMerkleCommitmentV2,
411 accounts: Arc<Vec<RewardAccountV2>>,
412 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
413 tracing::info!("Fetching RewardMerkleTreeV2 for height: {height}");
414
415 let response_validation_fn = move |_request: &Request, response: Response| {
417 let accounts = accounts.clone();
418 async move {
419 let Response::RewardMerkleTreeV2(tree_bytes) = response else {
421 return Err(anyhow::anyhow!("expected reward accounts response"));
422 };
423
424 let tree_data = bincode::deserialize::<RewardMerkleTreeV2Data>(&tree_bytes)
425 .context(
426 "Failed to deserialize RewardMerkleTreeV2 for height {height} from remote",
427 )?;
428
429 let reward_merkle_tree: PermittedRewardMerkleTreeV2 =
431 PermittedRewardMerkleTreeV2::try_from_kv_set(tree_data.balances).await?;
432
433 anyhow::ensure!(reward_merkle_tree.tree.commitment() == reward_merkle_tree_root);
434 anyhow::ensure!(!forgotten_accounts_include(&reward_merkle_tree, &accounts));
435
436 Ok(reward_merkle_tree)
437 }
438 };
439
440 let response = self
442 .request_indefinitely(
443 Request::RewardMerkleTreeV2(height, *view),
444 RequestType::Batched,
445 response_validation_fn,
446 )
447 .await
448 .with_context(|| "failed to request reward accounts")?;
449
450 tracing::info!("Fetched RewardMerkleTreeV2 for height: {height}");
451
452 Ok(response)
453 }
454
455 async fn fetch_reward_accounts_v1(
456 &self,
457 _instance: &NodeState,
458 height: u64,
459 view: ViewNumber,
460 reward_merkle_tree_root: RewardMerkleCommitmentV1,
461 accounts: Vec<RewardAccountV1>,
462 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
463 tracing::info!("Fetching v1 reward accounts for height: {height}, view: {view}");
464
465 let accounts_clone = accounts.clone();
467
468 let response_validation_fn = move |_request: &Request, response: Response| {
470 let accounts_clone = accounts_clone.clone();
472
473 async move {
474 let Response::RewardAccountsV1(reward_merkle_tree) = response else {
476 return Err(anyhow::anyhow!("expected v1 reward accounts response"));
477 };
478
479 let mut proofs = Vec::new();
481 for account in accounts_clone {
482 let (proof, _) =
483 RewardAccountProofV1::prove(&reward_merkle_tree, account.into())
484 .with_context(|| format!("response was missing account {account}"))?;
485 proof.verify(&reward_merkle_tree_root).with_context(|| {
486 format!(
487 "invalid proof for v1 reward account {account}, root: \
488 {reward_merkle_tree_root}"
489 )
490 })?;
491 proofs.push(proof);
492 }
493
494 Ok(proofs)
495 }
496 };
497
498 let response = self
500 .request_indefinitely(
501 Request::RewardAccountsV1(height, *view, accounts),
502 RequestType::Batched,
503 response_validation_fn,
504 )
505 .await
506 .with_context(|| "failed to request v1 reward accounts")?;
507
508 tracing::info!("Fetched v1 reward accounts for height: {height}, view: {view}");
509
510 Ok(response)
511 }
512
513 async fn fetch_state_cert(
514 &self,
515 epoch: u64,
516 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
517 tracing::info!("Fetching state cert for epoch: {epoch}");
518
519 let response_validation_fn = move |_request: &Request, response: Response| async move {
521 let Response::StateCert(state_cert) = response else {
523 return Err(anyhow::anyhow!("expected state cert response"));
524 };
525
526 Ok(state_cert)
527 };
528
529 let response = self
531 .request_indefinitely(
532 Request::StateCert(epoch),
533 RequestType::Batched,
534 response_validation_fn,
535 )
536 .await
537 .with_context(|| "failed to request state cert")?;
538
539 tracing::info!("Fetched state cert for epoch: {epoch}");
540
541 Ok(response)
542 }
543
544 fn is_local(&self) -> bool {
545 false
546 }
547}