1use alloc::vec::Vec;
2use codec::{ConstEncodedLen, Decode, Encode, Input, MaxEncodedLen};
3use jam_types::{Hash, ServiceId, MAX_PREIMAGE_BLOB_LEN};
4
5pub const MAX_FILE_LEN: u64 = 100 * 1024 * 1024;
7
8pub const MAX_FILE_BLOCK_LEN: u64 = MAX_PREIMAGE_BLOB_LEN as u64;
9pub const MIN_FILE_BLOCK_LEN: u64 = 32;
10
11#[derive(Clone, Encode, Debug)]
18pub struct FileBlock(Vec<u8>);
19
20impl FileBlock {
21 pub fn new(v: Vec<u8>) -> Option<Self> {
22 if v.len() < 32 {
23 return None;
25 }
26 Some(Self(v))
27 }
28 pub fn next(&self) -> Hash {
29 self.0[..32].try_into().expect("File block is always at least 32 bytes long")
30 }
31
32 pub fn data(&self) -> &[u8] {
33 &self.0[32..]
34 }
35
36 pub fn into_inner(self) -> Vec<u8> {
37 self.0
38 }
39}
40
41impl core::ops::Deref for FileBlock {
42 type Target = [u8];
43 fn deref(&self) -> &Self::Target {
44 self.0.as_slice()
45 }
46}
47
48impl AsRef<[u8]> for FileBlock {
49 fn as_ref(&self) -> &[u8] {
50 &self.0[..]
51 }
52}
53
54impl Decode for FileBlock {
55 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
56 let bytes: Vec<u8> = Decode::decode(input)?;
57 if !(MIN_FILE_BLOCK_LEN..=MAX_FILE_BLOCK_LEN).contains(&(bytes.len() as u64)) {
58 return Err("Invalid file block".into());
59 }
60 Ok(Self(bytes))
61 }
62}
63
64impl MaxEncodedLen for FileBlock {
65 fn max_encoded_len() -> usize {
66 MAX_PREIMAGE_BLOB_LEN
67 }
68}
69
70#[derive(Clone, Debug)]
74pub struct File {
75 blocks: Vec<(Hash, FileBlock)>,
76}
77
78impl File {
79 pub fn new(data: &[u8]) -> Self {
80 let iter = data.chunks(MAX_DATA_LEN);
81 let mut blocks = Vec::with_capacity(iter.len());
82 let mut next_hash = [0_u8; 32];
84 for data in iter.rev() {
85 let mut block = Vec::with_capacity(data.len() + 32);
86 block.extend_from_slice(&next_hash[..]);
87 block.extend_from_slice(data);
88 next_hash = hash_raw(&block);
89 blocks.push((next_hash, FileBlock(block)));
90 }
91 blocks.reverse();
92 Self { blocks }
93 }
94
95 pub fn read<F1, F2, E>(file: &FileRef, mut lookup: F1, make_error: F2) -> Result<Self, E>
100 where
101 F1: FnMut(ServiceId, &Hash) -> Result<Vec<u8>, E>,
102 F2: FnOnce() -> E,
103 {
104 let mut blocks = Vec::new();
105 let mut next_hash = file.hash;
106 let mut file_len = 0;
107 while next_hash != [0; 32] {
108 let block = lookup(file.service_id, &next_hash)?;
109 if !(MIN_FILE_BLOCK_LEN..=MAX_FILE_BLOCK_LEN).contains(&(block.len() as u64)) {
110 return Err(make_error());
112 }
113 if file_len + block.len() > MAX_FILE_LEN as usize {
114 return Err(make_error());
116 }
117 let next_next_hash = block[..32].try_into().expect("The length is 32");
118 file_len += block.len();
119 blocks.push((next_hash, FileBlock(block)));
120 next_hash = next_next_hash;
121 }
122 Ok(Self { blocks })
123 }
124
125 pub fn from_blocks(blocks: Vec<(Hash, FileBlock)>) -> Self {
126 Self { blocks }
127 }
128
129 pub fn first_hash(&self) -> Hash {
131 self.blocks[0].0
132 }
133
134 pub fn into_inner(self) -> Vec<(Hash, FileBlock)> {
135 self.blocks
136 }
137}
138
139impl core::ops::Deref for File {
140 type Target = [(Hash, FileBlock)];
141 fn deref(&self) -> &Self::Target {
142 self.blocks.as_slice()
143 }
144}
145
146fn hash_raw(data: &[u8]) -> Hash {
147 let h = blake2b_simd::Params::new().hash_length(32).hash(data);
148 h.as_bytes().try_into().expect("Hash length set to 32")
149}
150
151const MAX_DATA_LEN: usize = MAX_PREIMAGE_BLOB_LEN - 32;
152
153#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode, MaxEncodedLen, Debug)]
155pub struct FileRef {
156 pub service_id: ServiceId,
158 pub hash: Hash,
160}
161
162impl ConstEncodedLen for FileRef {}