monero_interface/
provides_blockchain.rs1use core::{ops::RangeInclusive, future::Future};
2use alloc::{format, vec::Vec, string::ToString};
3
4use monero_oxide::block::Block;
5
6use crate::{InterfaceError, ProvidesBlockchainMeta};
7
8pub trait ProvidesUnvalidatedBlockchain: Sync + ProvidesBlockchainMeta {
13 fn contiguous_blocks(
19 &self,
20 range: RangeInclusive<usize>,
21 ) -> impl Send + Future<Output = Result<Vec<Block>, InterfaceError>> {
22 async move {
23 let mut blocks =
26 Vec::with_capacity(range.end().saturating_sub(*range.start()).saturating_add(1));
27 for number in range {
28 blocks.push(self.block_by_number(number).await?);
29 }
30 Ok(blocks)
31 }
32 }
33
34 fn block(&self, hash: [u8; 32]) -> impl Send + Future<Output = Result<Block, InterfaceError>>;
44
45 fn block_by_number(
52 &self,
53 number: usize,
54 ) -> impl Send + Future<Output = Result<Block, InterfaceError>> {
55 async move {
56 let mut blocks = self.contiguous_blocks(number ..= number).await?;
57 if blocks.len() != 1 {
58 Err(InterfaceError::InternalError(format!(
59 "`{}` returned {} blocks, expected {}",
60 "ProvidesUnvalidatedBlockchain::contiguous_blocks",
61 blocks.len(),
62 1,
63 )))?;
64 }
65 Ok(blocks.pop().expect("verified we had a block"))
66 }
67 }
68
69 fn block_hash(
74 &self,
75 number: usize,
76 ) -> impl Send + Future<Output = Result<[u8; 32], InterfaceError>>;
77}
78
79pub trait ProvidesBlockchain: ProvidesBlockchainMeta {
81 fn contiguous_blocks(
86 &self,
87 range: RangeInclusive<usize>,
88 ) -> impl Send + Future<Output = Result<Vec<Block>, InterfaceError>>;
89
90 fn block(&self, hash: [u8; 32]) -> impl Send + Future<Output = Result<Block, InterfaceError>>;
94
95 fn block_by_number(
102 &self,
103 number: usize,
104 ) -> impl Send + Future<Output = Result<Block, InterfaceError>>;
105
106 fn block_hash(
111 &self,
112 number: usize,
113 ) -> impl Send + Future<Output = Result<[u8; 32], InterfaceError>>;
114}
115
116pub(crate) fn sanity_check_contiguous_blocks<'a>(
117 range: RangeInclusive<usize>,
118 blocks: impl Iterator<Item = &'a Block>,
119) -> Result<(), InterfaceError> {
120 let mut parent = None;
121 for (number, block) in range.zip(blocks) {
122 if block.number() != number {
123 Err(InterfaceError::InvalidInterface(format!(
124 "requested block #{number}, received #{}",
125 block.number()
126 )))?;
127 }
128
129 let block_hash = block.hash();
130 if let Some(parent) = parent.or((number == 0).then_some([0; 32])) {
131 if parent != block.header.previous {
132 Err(InterfaceError::InvalidInterface(
133 "
134 interface returned a block which doesn't build on the prior block \
135 when requesting a contiguous series
136 "
137 .to_string(),
138 ))?;
139 }
140 }
141 parent = Some(block_hash);
142 }
143 Ok(())
144}
145
146pub(crate) fn sanity_check_block_by_hash(
147 hash: &[u8; 32],
148 block: &Block,
149) -> Result<(), InterfaceError> {
150 let actual_hash = block.hash();
151 if &actual_hash != hash {
152 Err(InterfaceError::InvalidInterface(format!(
153 "requested block {}, received {}",
154 hex::encode(hash),
155 hex::encode(actual_hash)
156 )))?;
157 }
158
159 Ok(())
160}
161
162pub(crate) fn sanity_check_block_by_number(
163 number: usize,
164 block: &Block,
165) -> Result<(), InterfaceError> {
166 if block.number() != number {
167 Err(InterfaceError::InvalidInterface(format!(
168 "requested block #{number}, received #{}",
169 block.number()
170 )))?;
171 }
172 Ok(())
173}
174
175impl<P: ProvidesUnvalidatedBlockchain> ProvidesBlockchain for P {
176 fn contiguous_blocks(
177 &self,
178 range: RangeInclusive<usize>,
179 ) -> impl Send + Future<Output = Result<Vec<Block>, InterfaceError>> {
180 async move {
181 let blocks =
182 <P as ProvidesUnvalidatedBlockchain>::contiguous_blocks(self, range.clone()).await?;
183 let expected_blocks =
184 range.end().saturating_sub(*range.start()).checked_add(1).ok_or_else(|| {
185 InterfaceError::InternalError(
186 "amount of blocks requested wasn't representable in a `usize`".to_string(),
187 )
188 })?;
189 if blocks.len() != expected_blocks {
190 Err(InterfaceError::InternalError(format!(
191 "`{}` returned {} blocks, expected {}",
192 "ProvidesUnvalidatedBlockchain::contiguous_blocks",
193 blocks.len(),
194 expected_blocks,
195 )))?;
196 }
197 sanity_check_contiguous_blocks(range, blocks.iter())?;
198 Ok(blocks)
199 }
200 }
201
202 fn block(&self, hash: [u8; 32]) -> impl Send + Future<Output = Result<Block, InterfaceError>> {
203 async move {
204 let block = <P as ProvidesUnvalidatedBlockchain>::block(self, hash).await?;
205 sanity_check_block_by_hash(&hash, &block)?;
206 Ok(block)
207 }
208 }
209
210 fn block_by_number(
211 &self,
212 number: usize,
213 ) -> impl Send + Future<Output = Result<Block, InterfaceError>> {
214 async move {
215 let block = <P as ProvidesUnvalidatedBlockchain>::block_by_number(self, number).await?;
216 sanity_check_block_by_number(number, &block)?;
217 Ok(block)
218 }
219 }
220
221 fn block_hash(
222 &self,
223 number: usize,
224 ) -> impl Send + Future<Output = Result<[u8; 32], InterfaceError>> {
225 <P as ProvidesUnvalidatedBlockchain>::block_hash(self, number)
226 }
227}