tycho_core/storage/block/
package_entry.rs1use std::cmp::Ordering;
2use std::hash::Hash;
3
4use tycho_block_util::archive::ArchiveEntryType;
5use tycho_storage::kv::{StoredValue, StoredValueBuffer};
6use tycho_types::cell::HashBytes;
7use tycho_types::models::*;
8
9#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
10pub struct PartialBlockId {
11 pub shard: ShardIdent,
12 pub seqno: u32,
13 pub root_hash: HashBytes,
14}
15
16impl PartialBlockId {
17 pub fn as_short_id(&self) -> BlockIdShort {
18 BlockIdShort {
19 shard: self.shard,
20 seqno: self.seqno,
21 }
22 }
23
24 pub fn make_full(&self, file_hash: HashBytes) -> BlockId {
25 BlockId {
26 shard: self.shard,
27 seqno: self.seqno,
28 root_hash: self.root_hash,
29 file_hash,
30 }
31 }
32}
33
34impl From<BlockId> for PartialBlockId {
35 fn from(value: BlockId) -> Self {
36 Self {
37 shard: value.shard,
38 seqno: value.seqno,
39 root_hash: value.root_hash,
40 }
41 }
42}
43
44impl From<&BlockId> for PartialBlockId {
45 fn from(value: &BlockId) -> Self {
46 Self {
47 shard: value.shard,
48 seqno: value.seqno,
49 root_hash: value.root_hash,
50 }
51 }
52}
53
54impl StoredValue for PartialBlockId {
55 const SIZE_HINT: usize = 4 + 8 + 4 + 32;
56
57 type OnStackSlice = [u8; Self::SIZE_HINT];
58
59 fn serialize<T: StoredValueBuffer>(&self, buffer: &mut T) {
60 let mut result = [0; Self::SIZE_HINT];
61 result[..4].copy_from_slice(&self.shard.workchain().to_be_bytes());
62 result[4..12].copy_from_slice(&self.shard.prefix().to_be_bytes());
63 result[12..16].copy_from_slice(&self.seqno.to_be_bytes());
64 result[16..48].copy_from_slice(self.root_hash.as_slice());
65
66 buffer.write_raw_slice(&result);
67 }
68
69 fn deserialize(reader: &mut &[u8]) -> Self
70 where
71 Self: Sized,
72 {
73 assert_eq!(reader.len(), Self::SIZE_HINT, "invalid partial id");
74
75 let workchain = i32::from_be_bytes(reader[..4].try_into().unwrap());
76 let prefix = u64::from_be_bytes(reader[4..12].try_into().unwrap());
77 let seqno = u32::from_be_bytes(reader[12..16].try_into().unwrap());
78 let root_hash = HashBytes::from_slice(&reader[16..48]);
79
80 *reader = &reader[Self::SIZE_HINT..];
81
82 Self {
83 shard: ShardIdent::new(workchain, prefix).expect("invalid shard ident"),
84 seqno,
85 root_hash,
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
92pub struct PackageEntryKey {
93 pub block_id: PartialBlockId,
94 pub ty: ArchiveEntryType,
95}
96
97impl PackageEntryKey {
98 pub const SIZE_HINT: usize = 4 + 8 + 4 + 32 + 1; }
100
101impl cassadilia::KeyBytes for PackageEntryKey {
102 type Bytes = [u8; Self::SIZE_HINT];
103
104 fn to_key_bytes(&self) -> Self::Bytes {
105 let mut result = [0u8; Self::SIZE_HINT];
106 result[..4].copy_from_slice(&self.block_id.shard.workchain().to_be_bytes());
107 result[4..12].copy_from_slice(&self.block_id.shard.prefix().to_be_bytes());
108 result[12..16].copy_from_slice(&self.block_id.seqno.to_be_bytes());
109 result[16..48].copy_from_slice(self.block_id.root_hash.as_slice());
110 result[48] = self.ty as u8;
111 result
112 }
113
114 fn from_key_bytes(bytes: &[u8]) -> Option<Self> {
115 if bytes.len() != Self::SIZE_HINT {
116 return None;
117 }
118
119 let workchain = i32::from_be_bytes(bytes[..4].try_into().ok()?);
120 let prefix = u64::from_be_bytes(bytes[4..12].try_into().ok()?);
121 let seqno = u32::from_be_bytes(bytes[12..16].try_into().ok()?);
122 let root_hash = HashBytes::from_slice(&bytes[16..48]);
123 let ty = ArchiveEntryType::from_byte(bytes[48])?;
124
125 Some(Self {
126 block_id: PartialBlockId {
127 shard: ShardIdent::new(workchain, prefix)?,
128 seqno,
129 root_hash,
130 },
131 ty,
132 })
133 }
134}
135
136impl Ord for PackageEntryKey {
137 fn cmp(&self, other: &Self) -> Ordering {
138 let self_wc_ordered = self.block_id.shard.workchain() as u32;
143 let other_wc_ordered = other.block_id.shard.workchain() as u32;
144
145 (
146 self_wc_ordered,
147 self.block_id.shard.prefix(),
148 self.block_id.seqno,
149 self.block_id.root_hash,
150 self.ty as u8,
151 )
152 .cmp(&(
153 other_wc_ordered,
154 other.block_id.shard.prefix(),
155 other.block_id.seqno,
156 other.block_id.root_hash,
157 other.ty as u8,
158 ))
159 }
160}
161
162impl PartialOrd for PackageEntryKey {
163 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl PackageEntryKey {
169 pub fn block(block_id: &BlockId) -> Self {
170 Self {
171 block_id: block_id.into(),
172 ty: ArchiveEntryType::Block,
173 }
174 }
175
176 pub fn proof(block_id: &BlockId) -> Self {
177 Self {
178 block_id: block_id.into(),
179 ty: ArchiveEntryType::Proof,
180 }
181 }
182
183 pub fn queue_diff(block_id: &BlockId) -> Self {
184 Self {
185 block_id: block_id.into(),
186 ty: ArchiveEntryType::QueueDiff,
187 }
188 }
189}
190
191impl From<(BlockId, ArchiveEntryType)> for PackageEntryKey {
192 fn from((block_id, ty): (BlockId, ArchiveEntryType)) -> Self {
193 Self {
194 block_id: block_id.into(),
195 ty,
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use std::cmp::Ordering;
203
204 use super::*;
205
206 #[test]
207 fn ord_is_sane_and_matches_bytes() {
208 let to_bytes = |k: &PackageEntryKey| -> Vec<u8> {
210 let mut bytes = Vec::with_capacity(97);
211 bytes.extend_from_slice(&k.block_id.shard.workchain().to_be_bytes());
212 bytes.extend_from_slice(&k.block_id.shard.prefix().to_be_bytes());
213 bytes.extend_from_slice(&k.block_id.seqno.to_be_bytes());
214 bytes.extend_from_slice(&k.block_id.root_hash.0);
215 bytes.push(k.ty as u8);
216 bytes
217 };
218
219 let base = PackageEntryKey {
220 block_id: PartialBlockId {
221 shard: ShardIdent::BASECHAIN, seqno: 100,
223 root_hash: HashBytes([1; 32]),
224 },
225 ty: ArchiveEntryType::Block,
226 };
227
228 let check = |a: PackageEntryKey, b: PackageEntryKey, expected: Ordering| {
229 let rust_ord = a.cmp(&b);
230 let byte_ord = to_bytes(&a).cmp(&to_bytes(&b));
231 assert_eq!(rust_ord, byte_ord, "Rust Ord vs byte Ord mismatch");
233 assert_eq!(rust_ord, expected, "Unexpected ordering");
235 };
236
237 check(
241 PackageEntryKey {
242 block_id: PartialBlockId {
243 shard: ShardIdent::MASTERCHAIN,
244 ..base.block_id
245 },
246 ..base
247 },
248 base,
249 Ordering::Greater,
250 );
251
252 let (left_shard, right_shard) = base.block_id.shard.split().unwrap();
254 check(
255 PackageEntryKey {
256 block_id: PartialBlockId {
257 shard: left_shard,
258 ..base.block_id
259 },
260 ..base
261 },
262 PackageEntryKey {
263 block_id: PartialBlockId {
264 shard: right_shard,
265 ..base.block_id
266 },
267 ..base
268 },
269 Ordering::Less,
270 );
271
272 check(
274 base,
275 PackageEntryKey {
276 block_id: PartialBlockId {
277 seqno: base.block_id.seqno + 1,
278 ..base.block_id
279 },
280 ..base
281 },
282 Ordering::Less,
283 );
284
285 let mut bigger_hash = base.block_id.root_hash;
287 bigger_hash.0[31] = 2; check(
289 base,
290 PackageEntryKey {
291 block_id: PartialBlockId {
292 root_hash: bigger_hash,
293 ..base.block_id
294 },
295 ..base
296 },
297 Ordering::Less,
298 );
299
300 check(
302 base,
303 PackageEntryKey {
304 ty: ArchiveEntryType::Proof,
305 ..base
306 },
307 Ordering::Less,
308 );
309 }
310}