monero_interface/
provides_blockchain.rs1use core::{ops::RangeInclusive, future::Future};
2use alloc::{borrow::ToOwned as _, format, vec::Vec};
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
116#[expect(single_use_lifetimes)] pub(crate) fn sanity_check_contiguous_blocks<'block>(
118 range: RangeInclusive<usize>,
119 blocks: impl Iterator<Item = &'block Block>,
120) -> Result<(), InterfaceError> {
121 let mut parent = None;
122 for (number, block) in range.zip(blocks) {
123 if block.number() != number {
124 Err(InterfaceError::InvalidInterface(format!(
125 "requested block #{number}, received #{}",
126 block.number()
127 )))?;
128 }
129
130 let block_hash = block.hash();
131 if let Some(parent) = parent.or((number == 0).then_some([0; 32])) {
132 if parent != block.header.previous {
133 Err(InterfaceError::InvalidInterface(
134 "
135 interface returned a block which doesn't build on the prior block \
136 when requesting a contiguous series
137 "
138 .to_owned(),
139 ))?;
140 }
141 }
142 parent = Some(block_hash);
143 }
144 Ok(())
145}
146
147pub(crate) fn sanity_check_block_by_hash(
148 hash: &[u8; 32],
149 block: &Block,
150) -> Result<(), InterfaceError> {
151 let actual_hash = block.hash();
152 if &actual_hash != hash {
153 Err(InterfaceError::InvalidInterface(format!(
154 "requested block {}, received {}",
155 hex::encode(hash),
156 hex::encode(actual_hash)
157 )))?;
158 }
159
160 Ok(())
161}
162
163pub(crate) fn sanity_check_block_by_number(
164 number: usize,
165 block: &Block,
166) -> Result<(), InterfaceError> {
167 if block.number() != number {
168 Err(InterfaceError::InvalidInterface(format!(
169 "requested block #{number}, received #{}",
170 block.number()
171 )))?;
172 }
173 Ok(())
174}
175
176impl<P: ProvidesUnvalidatedBlockchain> ProvidesBlockchain for P {
177 fn contiguous_blocks(
178 &self,
179 range: RangeInclusive<usize>,
180 ) -> impl Send + Future<Output = Result<Vec<Block>, InterfaceError>> {
181 async move {
182 let blocks =
183 <P as ProvidesUnvalidatedBlockchain>::contiguous_blocks(self, range.clone()).await?;
184 let expected_blocks =
185 range.end().saturating_sub(*range.start()).checked_add(1).ok_or_else(|| {
186 InterfaceError::InternalError(
187 "amount of blocks requested wasn't representable in a `usize`".to_owned(),
188 )
189 })?;
190 if blocks.len() != expected_blocks {
191 Err(InterfaceError::InternalError(format!(
192 "`{}` returned {} blocks, expected {}",
193 "ProvidesUnvalidatedBlockchain::contiguous_blocks",
194 blocks.len(),
195 expected_blocks,
196 )))?;
197 }
198 sanity_check_contiguous_blocks(range, blocks.iter())?;
199 Ok(blocks)
200 }
201 }
202
203 fn block(&self, hash: [u8; 32]) -> impl Send + Future<Output = Result<Block, InterfaceError>> {
204 async move {
205 let block = <P as ProvidesUnvalidatedBlockchain>::block(self, hash).await?;
206 sanity_check_block_by_hash(&hash, &block)?;
207 Ok(block)
208 }
209 }
210
211 fn block_by_number(
212 &self,
213 number: usize,
214 ) -> impl Send + Future<Output = Result<Block, InterfaceError>> {
215 async move {
216 let block = <P as ProvidesUnvalidatedBlockchain>::block_by_number(self, number).await?;
217 sanity_check_block_by_number(number, &block)?;
218 Ok(block)
219 }
220 }
221
222 fn block_hash(
223 &self,
224 number: usize,
225 ) -> impl Send + Future<Output = Result<[u8; 32], InterfaceError>> {
226 <P as ProvidesUnvalidatedBlockchain>::block_hash(self, number)
227 }
228}