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