penumbra_sdk_tct/
index.rs

1//! Types to distinguish between different kinds of indices, to prevent them from being confused for
2//! each other internally.
3//!
4//! Methods that take `Into<u64>` as an index argument can be given types from the [`within`]
5//! module, which are all `Into<u64>`. They can be constructed from types in this module, which are
6//! all `From<u16>`.
7
8use serde::{Deserialize, Serialize};
9
10/// The index of an individual item in a block.
11///
12/// Create this using `From<u16>`.
13#[derive(
14    Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Derivative, Serialize, Deserialize,
15)]
16#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
17#[derivative(Debug = "transparent")]
18pub struct Commitment(u16);
19
20impl Commitment {
21    /// Increment the commitment.
22    pub fn increment(&mut self) {
23        self.0
24            .checked_add(1)
25            .expect("block index should never overflow");
26    }
27
28    /// The maximum representable commitment index.
29    pub const MAX: Self = Self(u16::MAX);
30}
31
32impl From<u16> for Commitment {
33    fn from(index: u16) -> Self {
34        Self(index)
35    }
36}
37
38impl From<Commitment> for u16 {
39    fn from(commitment: Commitment) -> Self {
40        commitment.0
41    }
42}
43
44/// The index of an individual block in an epoch.
45///
46/// Create this using `From<u16>`.
47#[derive(
48    Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Derivative, Serialize, Deserialize,
49)]
50#[derivative(Debug = "transparent")]
51#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
52pub struct Block(u16);
53
54impl From<u16> for Block {
55    fn from(index: u16) -> Self {
56        Self(index)
57    }
58}
59
60impl From<Block> for u16 {
61    fn from(block: Block) -> Self {
62        block.0
63    }
64}
65
66impl Block {
67    /// Increment the block.
68    pub fn increment(&mut self) {
69        self.0
70            .checked_add(1)
71            .expect("block index should never overflow");
72    }
73
74    /// The maximum representable block index.
75    pub const MAX: Self = Self(u16::MAX);
76}
77
78/// The index of an individual epoch in a tree.
79///
80/// Create this using `From<u16>`.
81#[derive(
82    Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Derivative, Serialize, Deserialize,
83)]
84#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
85#[derivative(Debug = "transparent")]
86pub struct Epoch(u16);
87
88impl From<u16> for Epoch {
89    fn from(index: u16) -> Self {
90        Self(index)
91    }
92}
93
94impl From<Epoch> for u16 {
95    fn from(epoch: Epoch) -> Self {
96        epoch.0
97    }
98}
99
100impl Epoch {
101    /// Increment the epoch.
102    pub fn increment(&mut self) {
103        self.0
104            .checked_add(1)
105            .expect("block index should never overflow");
106    }
107
108    /// The maximum epoch index representable.
109    pub const MAX: Self = Self(u16::MAX);
110}
111
112/// Indices of individual items within larger structures.
113pub mod within {
114    use super::*;
115
116    /// The index of an individual item within a block.
117    #[derive(
118        Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
119    )]
120    #[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
121    pub struct Block {
122        /// The index of the item within its block.
123        pub commitment: super::Commitment,
124    }
125
126    impl Block {
127        /// The maximum representable index within a block.
128        pub const MAX: Self = Self {
129            commitment: Commitment::MAX,
130        };
131    }
132
133    impl From<Block> for u16 {
134        fn from(
135            Block {
136                commitment: Commitment(item),
137            }: Block,
138        ) -> Self {
139            item
140        }
141    }
142
143    impl From<u16> for Block {
144        fn from(position: u16) -> Self {
145            Self {
146                commitment: Commitment(position),
147            }
148        }
149    }
150
151    impl From<Block> for u64 {
152        fn from(block: Block) -> Self {
153            u16::from(block) as u64
154        }
155    }
156
157    /// The index of an individual item within an epoch.
158    #[derive(
159        Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
160    )]
161    #[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
162    pub struct Epoch {
163        /// The index of the block within its epoch.
164        pub block: super::Block,
165        /// The index of the item within its block.
166        pub commitment: super::Commitment,
167    }
168
169    impl Epoch {
170        /// The maximum representable index within an epoch.
171        pub const MAX: Self = Self {
172            block: super::Block::MAX,
173            commitment: Commitment::MAX,
174        };
175    }
176
177    impl From<Epoch> for u32 {
178        fn from(
179            Epoch {
180                block: super::Block(block),
181                commitment: Commitment(item),
182            }: Epoch,
183        ) -> Self {
184            ((block as u32) << 16) | item as u32
185        }
186    }
187
188    impl From<u32> for Epoch {
189        fn from(position: u32) -> Self {
190            let block = (position >> 16) as u16;
191            let commitment = position as u16;
192            Self {
193                block: super::Block(block),
194                commitment: Commitment(commitment),
195            }
196        }
197    }
198
199    impl From<Epoch> for u64 {
200        fn from(epoch: Epoch) -> Self {
201            u32::from(epoch) as u64
202        }
203    }
204
205    /// The index of an individual item within a tree.
206    #[derive(
207        Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
208    )]
209    #[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
210    pub struct Tree {
211        /// The index of the epoch within its tree.
212        pub epoch: super::Epoch,
213        /// The index of the block within its epoch.
214        pub block: super::Block,
215        /// The index of the item within its block.
216        pub commitment: super::Commitment,
217    }
218
219    impl Tree {
220        /// The maximum representable index within a tree.
221        pub const MAX: Self = Self {
222            epoch: super::Epoch::MAX,
223            block: super::Block::MAX,
224            commitment: Commitment::MAX,
225        };
226    }
227
228    impl From<Tree> for u64 {
229        fn from(
230            Tree {
231                epoch: super::Epoch(epoch),
232                block: super::Block(block),
233                commitment: super::Commitment(item),
234            }: Tree,
235        ) -> Self {
236            ((epoch as u64) << 32) | ((block as u64) << 16) | item as u64
237        }
238    }
239
240    impl From<u64> for Tree {
241        fn from(position: u64) -> Self {
242            let epoch = (position >> 32) as u16;
243            let block = (position >> 16) as u16;
244            let commitment = position as u16;
245            Self {
246                epoch: super::Epoch(epoch),
247                block: super::Block(block),
248                commitment: super::Commitment(commitment),
249            }
250        }
251    }
252}
253
254#[cfg(test)]
255mod test {
256    use super::*;
257    use proptest::prelude::*;
258
259    proptest! {
260        #[test]
261        fn u64_convert_eternity_inverse(e in 0u16..u16::MAX, b in 0u16..u16::MAX, c in 0u16..u16::MAX) {
262            let tree = within::Tree { epoch: e.into(), block: b.into(), commitment: c.into() };
263            let position: u64 = tree.into();
264            let back_again = position.into();
265            assert_eq!(tree, back_again);
266        }
267
268        #[test]
269        fn u32_convert_epoch_inverse(b in 0u16..u16::MAX, c in 0u16..u16::MAX) {
270            let epoch = within::Epoch { block: b.into(), commitment: c.into() };
271            let position: u32 = epoch.into();
272            let back_again = position.into();
273            assert_eq!(epoch, back_again);
274        }
275
276        #[test]
277        fn u16_convert_block_inverse(c in 0u16..u16::MAX) {
278            let block = within::Block { commitment: c.into() };
279            let position: u16 = block.into();
280            let back_again = position.into();
281            assert_eq!(block, back_again);
282        }
283    }
284}