Skip to main content

amaru_protocols/protocol_messages/
version_table.rs

1// Copyright 2025 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    collections::BTreeMap,
17    fmt,
18    fmt::{Debug, Display},
19};
20
21use amaru_kernel::{NetworkMagic, cbor};
22
23use crate::protocol_messages::{
24    version_data::{PEER_SHARING_DISABLED, VersionData},
25    version_number::VersionNumber,
26};
27
28#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
29pub struct VersionTable<T> {
30    pub values: BTreeMap<VersionNumber, T>,
31}
32
33impl VersionTable<VersionData> {
34    pub fn empty() -> VersionTable<VersionData> {
35        VersionTable { values: BTreeMap::new() }
36    }
37
38    pub fn query(network_magic: NetworkMagic) -> VersionTable<VersionData> {
39        VersionTable {
40            values: vec![
41                (VersionNumber::V11, VersionData::new(network_magic, false, PEER_SHARING_DISABLED, true)),
42                (VersionNumber::V12, VersionData::new(network_magic, false, PEER_SHARING_DISABLED, true)),
43                (VersionNumber::V13, VersionData::new(network_magic, false, PEER_SHARING_DISABLED, true)),
44                (VersionNumber::V14, VersionData::new(network_magic, false, PEER_SHARING_DISABLED, true)),
45            ]
46            .into_iter()
47            .collect::<BTreeMap<VersionNumber, VersionData>>(),
48        }
49    }
50
51    pub fn v11_and_above(
52        network_magic: NetworkMagic,
53        initiator_only_diffusion_mode: bool,
54    ) -> VersionTable<VersionData> {
55        let values = vec![
56            (
57                VersionNumber::V11,
58                VersionData::new(network_magic, initiator_only_diffusion_mode, PEER_SHARING_DISABLED, false),
59            ),
60            (
61                VersionNumber::V12,
62                VersionData::new(network_magic, initiator_only_diffusion_mode, PEER_SHARING_DISABLED, false),
63            ),
64            (
65                VersionNumber::V13,
66                VersionData::new(network_magic, initiator_only_diffusion_mode, PEER_SHARING_DISABLED, false),
67            ),
68            (
69                VersionNumber::V14,
70                VersionData::new(network_magic, initiator_only_diffusion_mode, PEER_SHARING_DISABLED, false),
71            ),
72        ]
73        .into_iter()
74        .collect::<BTreeMap<VersionNumber, VersionData>>();
75
76        VersionTable { values }
77    }
78}
79
80impl<T: Display + Ord> Display for VersionTable<T> {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        let mut entries = self.values.iter().collect::<Vec<_>>();
83        entries.sort();
84        for (idx, (version, data)) in entries.into_iter().enumerate() {
85            if idx > 0 {
86                write!(f, ", ")?;
87            }
88            write!(f, "{}: {}", version.as_u64(), data)?;
89        }
90        Ok(())
91    }
92}
93
94impl<T> cbor::Encode<()> for VersionTable<T>
95where
96    T: fmt::Debug + Clone + cbor::Encode<VersionNumber>,
97{
98    fn encode<W: cbor::encode::Write>(
99        &self,
100        e: &mut cbor::Encoder<W>,
101        _ctx: &mut (),
102    ) -> Result<(), cbor::encode::Error<W::Error>> {
103        e.map(self.values.len() as u64)?;
104
105        for key in self.values.keys() {
106            e.encode(key)?;
107            let mut ctx = *key;
108            e.encode_with(&self.values[key], &mut ctx)?;
109        }
110
111        Ok(())
112    }
113}
114
115impl<'b, T> cbor::Decode<'b, ()> for VersionTable<T>
116where
117    T: fmt::Debug + Clone + cbor::Decode<'b, VersionNumber>,
118{
119    fn decode(d: &mut cbor::Decoder<'b>, _ctx: &mut ()) -> Result<Self, cbor::decode::Error> {
120        let len = d.map()?.ok_or(cbor::decode::Error::message("expected def-length map for versiontable"))?;
121        let mut values = BTreeMap::new();
122
123        for _ in 0..len {
124            let key = d.decode()?;
125            let mut ctx = key;
126            let value = d.decode_with(&mut ctx)?;
127            values.insert(key, value);
128        }
129        Ok(VersionTable { values })
130    }
131}
132
133#[cfg(test)]
134pub(crate) mod tests {
135    use amaru_kernel::prop_cbor_roundtrip;
136    use proptest::prop_compose;
137
138    use super::*;
139    use crate::protocol_messages::{
140        version_data::{VersionData, tests::any_version_data},
141        version_number::tests::any_version_number,
142    };
143
144    prop_cbor_roundtrip!(VersionTable<VersionData>, any_version_table());
145
146    prop_compose! {
147        pub fn any_version_table()(values in proptest::collection::btree_map(any_version_number(), any_version_data(), 0..3)) -> VersionTable<VersionData> {
148            VersionTable { values }
149        }
150    }
151}