cfxcore/pos/state_sync/chunk_request.rs
1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use diem_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version};
9use serde::{Deserialize, Serialize};
10use std::fmt;
11
12#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
13/// We're currently considering several types of chunk requests depending on the
14/// information available on the requesting side.
15pub enum TargetType {
16 /// The response is built relative to the target (or end of epoch).
17 /// **DEPRECATED**: `TargetLedgerInfo` is only required for backward
18 /// compatibility. State sync avoids sending these target types and
19 /// instead uses `HighestAvailable` below. This message will be removed on the next breaking release: <https://github.com/diem/diem/issues/8013>
20 TargetLedgerInfo(LedgerInfoWithSignatures),
21 /// The response is built relative to the highest available LedgerInfo (or
22 /// end of epoch). The value specifies the timeout in ms to wait for an
23 /// available response. This "long poll" approach allows a responding
24 /// node to add the request to the list of its subscriptions for the
25 /// duration of a timeout until some new information becomes available.
26 ///
27 /// `target_li`: While asking for the highest available ledger info, this
28 /// request also provides the option to the sync requester to specify a
29 /// target LI. This is to support the scenario where the sync requester
30 /// is lagging too much behind the responding node in the sync process.
31 /// If the highest ledger info version keeps advancing on the responding
32 /// node, even though the sync requester continues to receive and sync
33 /// txns, those txns will never be backed an LI, since a LI can only be
34 /// committed once all the transactions up to the LI's version has been
35 /// received. (It is important for a transaction to be backed by an LI,
36 /// because transactions need to be backed by an LI to be shown as
37 /// committed upon storage query) To prevent the above problem where
38 /// the transactions are never backed by a LI during sync catch-up
39 /// (or the difference between synced version and committed LI version
40 /// keeps growing on sync requester), this `TargetType` can
41 /// simultaneously (1) ask for the highest ledger info, and (2) specify a
42 /// target to build the requested transactions w.r.t.. With (1), the
43 /// sync requester can store the LI later to target-sync once it is
44 /// ready for that LI after syncing to an earlier target LI via (2).
45 ///
46 /// If `target_li` is not specified, the responding node will build the
47 /// responses against its highest LI
48 HighestAvailable {
49 target_li: Option<LedgerInfoWithSignatures>,
50 timeout_ms: u64,
51 },
52 /// The response is built relative to a LedgerInfo at a given version.
53 Waypoint(Version),
54}
55
56impl TargetType {
57 pub fn epoch(&self) -> Option<u64> {
58 match self {
59 TargetType::TargetLedgerInfo(li) => Some(li.ledger_info().epoch()),
60 TargetType::HighestAvailable { target_li, .. } => {
61 target_li.as_ref().map(|li| li.ledger_info().epoch())
62 }
63 TargetType::Waypoint(_) => None,
64 }
65 }
66
67 pub fn version(&self) -> Option<u64> {
68 match self {
69 TargetType::TargetLedgerInfo(li) => {
70 Some(li.ledger_info().version())
71 }
72 TargetType::HighestAvailable { target_li, .. } => {
73 target_li.as_ref().map(|li| li.ledger_info().version())
74 }
75 TargetType::Waypoint(version) => Some(*version),
76 }
77 }
78}
79
80impl fmt::Debug for TargetType {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 write!(f, "{}", self)
83 }
84}
85
86impl fmt::Display for TargetType {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 match self {
89 TargetType::TargetLedgerInfo(ledger_info) => {
90 write!(f, "TargetLedgerInfo({})", ledger_info)
91 }
92 TargetType::HighestAvailable {
93 target_li,
94 timeout_ms,
95 } => write!(
96 f,
97 "HighestAvailable(timeout:{}, target_li:{})",
98 timeout_ms,
99 target_li
100 .as_ref()
101 .map_or_else(|| String::from("None"), |li| li.to_string())
102 ),
103 TargetType::Waypoint(version) => write!(f, "Waypoint({})", version),
104 }
105 }
106}
107
108#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
109pub struct GetChunkRequest {
110 /// The response should start with `known_version + 1`.
111 pub known_version: Version,
112 /// Epoch the chunk response is supposed to belong to (i.e., epoch of
113 /// known_version + 1).
114 pub current_epoch: u64,
115 /// Max size of a chunk response.
116 pub limit: u64,
117 /// The target of the given request.
118 pub target: TargetType,
119}
120
121impl GetChunkRequest {
122 pub fn new(
123 known_version: Version, current_epoch: u64, limit: u64,
124 target: TargetType,
125 ) -> Self {
126 Self {
127 known_version,
128 current_epoch,
129 limit,
130 target,
131 }
132 }
133}
134
135impl fmt::Debug for GetChunkRequest {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 write!(f, "{}", self)
138 }
139}
140
141impl fmt::Display for GetChunkRequest {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 write!(
144 f,
145 "[ChunkRequest: known version: {}, epoch: {}, limit: {}, target: {}]",
146 self.known_version, self.current_epoch, self.limit, self.target,
147 )
148 }
149}