appcore_transfer/
descriptor.rs1use crate::{TransferError as E, TransferResult};
13use serde::{Deserialize, Serialize};
14use sha2::{Digest, Sha256};
15use std::io::Read;
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct TransferLimits {
19 pub max_object_bytes: u64,
21 pub max_block_bytes: u32,
23 pub max_blocks: usize,
25 pub max_files: usize,
27 pub max_store_bytes: u64,
29}
30impl Default for TransferLimits {
31 fn default() -> Self {
32 Self {
33 max_object_bytes: 64 * 1024 * 1024,
34 max_block_bytes: 64 * 1024,
35 max_blocks: 4096,
36 max_files: 16384,
37 max_store_bytes: 256 * 1024 * 1024,
38 }
39 }
40}
41impl TransferLimits {
42 pub fn validate(&self) -> TransferResult<()> {
44 if self.max_object_bytes == 0
45 || self.max_object_bytes > 256 * 1024 * 1024
46 || self.max_block_bytes == 0
47 || self.max_block_bytes > 1024 * 1024
48 || self.max_blocks == 0
49 || self.max_blocks > 4096
50 || self.max_files < 4
51 || self.max_files > 65536
52 || self.max_store_bytes == 0
53 {
54 return Err(E::InvalidContract);
55 }
56 Ok(())
57 }
58}
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(try_from = "DescriptorWire")]
63pub struct ObjectDescriptor {
64 format_version: u16,
65 object_hash: String,
66 total_bytes: u64,
67 block_bytes: u32,
68 block_hashes: Vec<String>,
69}
70#[derive(Deserialize)]
71#[serde(deny_unknown_fields)]
72pub(crate) struct DescriptorWire {
73 format_version: u16,
74 object_hash: String,
75 total_bytes: u64,
76 block_bytes: u32,
77 #[serde(deserialize_with = "deserialize_hashes")]
78 block_hashes: Vec<String>,
79}
80impl TryFrom<DescriptorWire> for ObjectDescriptor {
81 type Error = E;
82 fn try_from(wire: DescriptorWire) -> TransferResult<Self> {
83 let value = Self {
84 format_version: wire.format_version,
85 object_hash: wire.object_hash,
86 total_bytes: wire.total_bytes,
87 block_bytes: wire.block_bytes,
88 block_hashes: wire.block_hashes,
89 };
90 value.validate(TransferLimits {
91 max_object_bytes: 256 * 1024 * 1024,
92 max_block_bytes: 1024 * 1024,
93 ..TransferLimits::default()
94 })?;
95 Ok(value)
96 }
97}
98impl ObjectDescriptor {
99 pub fn new(
101 object_hash: String,
102 total_bytes: u64,
103 block_bytes: u32,
104 block_hashes: Vec<String>,
105 limits: TransferLimits,
106 ) -> TransferResult<Self> {
107 let value = Self {
108 format_version: 1,
109 object_hash,
110 total_bytes,
111 block_bytes,
112 block_hashes,
113 };
114 value.validate(limits)?;
115 Ok(value)
116 }
117 pub fn from_reader(
120 mut reader: impl Read,
121 total_bytes: u64,
122 block_bytes: u32,
123 limits: TransferLimits,
124 ) -> TransferResult<Self> {
125 limits.validate()?;
126 if total_bytes == 0
127 || total_bytes > limits.max_object_bytes
128 || block_bytes == 0
129 || block_bytes > limits.max_block_bytes
130 {
131 return Err(E::Limit);
132 }
133 let blocks = ((total_bytes - 1) / u64::from(block_bytes) + 1) as usize;
134 if blocks > limits.max_blocks {
135 return Err(E::Limit);
136 }
137 let mut full = Sha256::new();
138 let mut hashes = Vec::with_capacity(blocks);
139 let mut remaining = total_bytes;
140 let mut buffer = vec![0; block_bytes as usize];
141 while remaining > 0 {
142 let length = remaining.min(u64::from(block_bytes)) as usize;
143 reader
144 .read_exact(&mut buffer[..length])
145 .map_err(|_| E::Source)?;
146 full.update(&buffer[..length]);
147 hashes.push(hash(&buffer[..length]));
148 remaining -= length as u64;
149 }
150 if reader.read(&mut [0]).map_err(|_| E::Source)? != 0 {
151 return Err(E::Integrity);
152 }
153 Self::new(
154 hex(&full.finalize()),
155 total_bytes,
156 block_bytes,
157 hashes,
158 limits,
159 )
160 }
161 pub fn validate(&self, limits: TransferLimits) -> TransferResult<()> {
163 limits.validate()?;
164 if self.format_version != 1 {
165 return Err(E::UnsupportedVersion);
166 }
167 if self.total_bytes == 0
168 || self.total_bytes > limits.max_object_bytes
169 || self.block_bytes == 0
170 || self.block_bytes > limits.max_block_bytes
171 {
172 return Err(E::Limit);
173 }
174 let expected = ((self.total_bytes - 1) / u64::from(self.block_bytes) + 1) as usize;
175 if expected > limits.max_blocks || self.block_hashes.len() != expected {
176 return Err(E::Limit);
177 }
178 if !valid_hash(&self.object_hash) || self.block_hashes.iter().any(|h| !valid_hash(h)) {
179 return Err(E::InvalidContract);
180 }
181 Ok(())
182 }
183 pub fn from_json(bytes: &[u8], limits: TransferLimits) -> TransferResult<Self> {
185 if bytes.len() > 1024 * 1024 {
186 return Err(E::Limit);
187 }
188 let wire: DescriptorWire = serde_json::from_slice(bytes).map_err(|_| E::InvalidContract)?;
189 let value = Self::try_from(wire)?;
190 value.validate(limits)?;
191 Ok(value)
192 }
193 pub fn to_json(&self) -> TransferResult<Vec<u8>> {
195 serde_json::to_vec(self).map_err(|_| E::InvalidContract)
196 }
197 pub fn object_hash(&self) -> &str {
199 &self.object_hash
200 }
201 pub fn total_bytes(&self) -> u64 {
203 self.total_bytes
204 }
205 pub fn block_bytes(&self) -> u32 {
207 self.block_bytes
208 }
209 pub fn block_count(&self) -> usize {
211 self.block_hashes.len()
212 }
213 pub fn block_len(&self, index: usize) -> TransferResult<usize> {
215 if index >= self.block_count() {
216 return Err(E::InvalidContract);
217 }
218 Ok(
219 (self.total_bytes - index as u64 * u64::from(self.block_bytes))
220 .min(u64::from(self.block_bytes)) as usize,
221 )
222 }
223 pub fn block_offset(&self, index: usize) -> TransferResult<u64> {
225 self.block_len(index)?;
226 Ok(index as u64 * u64::from(self.block_bytes))
227 }
228 pub fn verify_block(&self, index: usize, bytes: &[u8]) -> TransferResult<()> {
230 if bytes.len() != self.block_len(index)? || hash(bytes) != self.block_hashes[index] {
231 return Err(E::Integrity);
232 }
233 Ok(())
234 }
235}
236pub(crate) fn hash(bytes: &[u8]) -> String {
237 hex(&Sha256::digest(bytes))
238}
239pub(crate) fn valid_hash(value: &str) -> bool {
240 value.len() == 64
241 && value
242 .bytes()
243 .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
244}
245
246pub(crate) fn hex(bytes: &[u8]) -> String {
247 bytes.iter().map(|b| format!("{b:02x}")).collect()
248}
249
250fn deserialize_hashes<'de, D: serde::Deserializer<'de>>(
251 deserializer: D,
252) -> Result<Vec<String>, D::Error> {
253 struct Hashes;
254 impl<'de> serde::de::Visitor<'de> for Hashes {
255 type Value = Vec<String>;
256 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 f.write_str("at most 4096 SHA-256 hashes")
258 }
259 fn visit_seq<A: serde::de::SeqAccess<'de>>(
260 self,
261 mut seq: A,
262 ) -> Result<Self::Value, A::Error> {
263 let mut hashes = Vec::new();
264 while let Some(value) = seq.next_element::<String>()? {
265 if hashes.len() >= 4096 || !valid_hash(&value) {
266 return Err(serde::de::Error::custom("invalid block hash inventory"));
267 }
268 hashes.push(value);
269 }
270 Ok(hashes)
271 }
272 }
273 deserializer.deserialize_seq(Hashes)
274}