1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Copyright 2021 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

use diem_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version};
use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
/// We're currently considering several types of chunk requests depending on the
/// information available on the requesting side.
pub enum TargetType {
    /// The response is built relative to the target (or end of epoch).
    /// **DEPRECATED**: `TargetLedgerInfo` is only required for backward
    /// compatibility. State sync avoids sending these target types and
    /// instead uses `HighestAvailable` below. This message will be removed on the next breaking release: <https://github.com/diem/diem/issues/8013>
    TargetLedgerInfo(LedgerInfoWithSignatures),
    /// The response is built relative to the highest available LedgerInfo (or
    /// end of epoch). The value specifies the timeout in ms to wait for an
    /// available response. This "long poll" approach allows a responding
    /// node to add the request to the list of its subscriptions for the
    /// duration of a timeout until some new information becomes available.
    ///
    /// `target_li`: While asking for the highest available ledger info, this
    /// request also provides the option to the sync requester to specify a
    /// target LI. This is to support the scenario where the sync requester
    /// is lagging too much behind the responding node in the sync process.
    /// If the highest ledger info version keeps advancing on the responding
    /// node, even though the sync requester continues to receive and sync
    /// txns, those txns will never be backed an LI, since a LI can only be
    /// committed once all the transactions up to the LI's version has been
    /// received. (It is important for a transaction to be backed by an LI,
    /// because transactions need to be backed by an LI to be shown as
    /// committed upon storage query) To prevent the above problem where
    /// the transactions are never backed by a LI during sync catch-up
    /// (or the difference between synced version and committed LI version
    /// keeps growing on sync requester), this `TargetType` can
    /// simultaneously (1) ask for the highest ledger info, and (2) specify a
    /// target to build the requested transactions w.r.t.. With (1), the
    /// sync requester can store the LI later to target-sync once it is
    /// ready for that LI after syncing to an earlier target LI via (2).
    ///
    /// If `target_li` is not specified, the responding node will build the
    /// responses against its highest LI
    HighestAvailable {
        target_li: Option<LedgerInfoWithSignatures>,
        timeout_ms: u64,
    },
    /// The response is built relative to a LedgerInfo at a given version.
    Waypoint(Version),
}

impl TargetType {
    pub fn epoch(&self) -> Option<u64> {
        match self {
            TargetType::TargetLedgerInfo(li) => Some(li.ledger_info().epoch()),
            TargetType::HighestAvailable { target_li, .. } => {
                target_li.as_ref().map(|li| li.ledger_info().epoch())
            }
            TargetType::Waypoint(_) => None,
        }
    }

    pub fn version(&self) -> Option<u64> {
        match self {
            TargetType::TargetLedgerInfo(li) => {
                Some(li.ledger_info().version())
            }
            TargetType::HighestAvailable { target_li, .. } => {
                target_li.as_ref().map(|li| li.ledger_info().version())
            }
            TargetType::Waypoint(version) => Some(*version),
        }
    }
}

impl fmt::Debug for TargetType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}

impl fmt::Display for TargetType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TargetType::TargetLedgerInfo(ledger_info) => {
                write!(f, "TargetLedgerInfo({})", ledger_info)
            }
            TargetType::HighestAvailable {
                target_li,
                timeout_ms,
            } => write!(
                f,
                "HighestAvailable(timeout:{}, target_li:{})",
                timeout_ms,
                target_li
                    .as_ref()
                    .map_or_else(|| String::from("None"), |li| li.to_string())
            ),
            TargetType::Waypoint(version) => write!(f, "Waypoint({})", version),
        }
    }
}

#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
pub struct GetChunkRequest {
    /// The response should start with `known_version + 1`.
    pub known_version: Version,
    /// Epoch the chunk response is supposed to belong to (i.e., epoch of
    /// known_version + 1).
    pub current_epoch: u64,
    /// Max size of a chunk response.
    pub limit: u64,
    /// The target of the given request.
    pub target: TargetType,
}

impl GetChunkRequest {
    pub fn new(
        known_version: Version, current_epoch: u64, limit: u64,
        target: TargetType,
    ) -> Self {
        Self {
            known_version,
            current_epoch,
            limit,
            target,
        }
    }
}

impl fmt::Debug for GetChunkRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}

impl fmt::Display for GetChunkRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "[ChunkRequest: known version: {}, epoch: {}, limit: {}, target: {}]",
            self.known_version, self.current_epoch, self.limit, self.target,
        )
    }
}