1use std::{sync::Arc, time::Duration};
7
8use custom_debug_derive::Debug;
9use linera_base::{
10 data_types::{Amount, ArithmeticError, BlobContent},
11 ensure,
12 identifiers::AccountOwner,
13};
14use linera_views::{context::Context, views::ViewError};
15use serde::Serialize;
16
17use crate::{ExecutionError, Message, Operation, ResourceControlPolicy, SystemExecutionStateView};
18
19#[derive(Clone, Debug, Default)]
20pub struct ResourceController<Account = Amount, Tracker = ResourceTracker> {
21 pub policy: Arc<ResourceControlPolicy>,
23 pub tracker: Tracker,
25 pub account: Account,
27}
28
29#[derive(Copy, Debug, Clone, Default)]
31pub struct ResourceTracker {
32 pub blocks: u32,
34 pub block_size: u64,
36 pub fuel: u64,
38 pub read_operations: u32,
40 pub write_operations: u32,
42 pub bytes_read: u64,
44 pub bytes_written: u64,
46 pub blobs_read: u32,
48 pub blobs_published: u32,
50 pub blob_bytes_read: u64,
52 pub blob_bytes_published: u64,
54 pub bytes_stored: i32,
56 pub operations: u32,
58 pub operation_bytes: u64,
60 pub messages: u32,
62 pub message_bytes: u64,
64 pub http_requests: u32,
66 pub service_oracle_queries: u32,
68 pub service_oracle_execution: Duration,
70 pub grants: Amount,
72}
73
74pub trait BalanceHolder {
76 fn balance(&self) -> Result<Amount, ArithmeticError>;
77
78 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
79
80 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
81}
82
83impl<Account, Tracker> ResourceController<Account, Tracker>
85where
86 Account: BalanceHolder,
87 Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
88{
89 pub fn balance(&self) -> Result<Amount, ArithmeticError> {
92 self.account.balance()
93 }
94
95 pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
98 if other <= initial {
99 self.account
100 .try_sub_assign(initial.try_sub(other).expect("other <= initial"))
101 .map_err(|_| ExecutionError::InsufficientFundingForFees {
102 balance: self.balance().unwrap_or(Amount::MAX),
103 })?;
104 } else {
105 self.account
106 .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
107 }
108 Ok(())
109 }
110
111 fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
113 self.account.try_sub_assign(fees).map_err(|_| {
114 ExecutionError::InsufficientFundingForFees {
115 balance: self.balance().unwrap_or(Amount::MAX),
116 }
117 })?;
118 Ok(())
119 }
120
121 pub(crate) fn remaining_fuel(&self) -> u64 {
123 self.policy
124 .remaining_fuel(self.balance().unwrap_or(Amount::MAX))
125 .min(
126 self.policy
127 .maximum_fuel_per_block
128 .saturating_sub(self.tracker.as_ref().fuel),
129 )
130 }
131
132 pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
134 self.tracker.as_mut().grants.try_add_assign(grant)?;
135 self.update_balance(grant)
136 }
137
138 pub fn track_block(&mut self) -> Result<(), ExecutionError> {
140 self.tracker.as_mut().blocks = self
141 .tracker
142 .as_mut()
143 .blocks
144 .checked_add(1)
145 .ok_or(ArithmeticError::Overflow)?;
146 self.update_balance(self.policy.block)
147 }
148
149 pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
151 self.tracker.as_mut().operations = self
152 .tracker
153 .as_mut()
154 .operations
155 .checked_add(1)
156 .ok_or(ArithmeticError::Overflow)?;
157 self.update_balance(self.policy.operation)?;
158 match operation {
159 Operation::System(_) => Ok(()),
160 Operation::User { bytes, .. } => {
161 let size = bytes.len();
162 self.tracker.as_mut().operation_bytes = self
163 .tracker
164 .as_mut()
165 .operation_bytes
166 .checked_add(size as u64)
167 .ok_or(ArithmeticError::Overflow)?;
168 self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
169 Ok(())
170 }
171 }
172 }
173
174 pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
176 self.tracker.as_mut().messages = self
177 .tracker
178 .as_mut()
179 .messages
180 .checked_add(1)
181 .ok_or(ArithmeticError::Overflow)?;
182 self.update_balance(self.policy.message)?;
183 match message {
184 Message::System(_) => Ok(()),
185 Message::User { bytes, .. } => {
186 let size = bytes.len();
187 self.tracker.as_mut().message_bytes = self
188 .tracker
189 .as_mut()
190 .message_bytes
191 .checked_add(size as u64)
192 .ok_or(ArithmeticError::Overflow)?;
193 self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
194 Ok(())
195 }
196 }
197 }
198
199 pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
201 self.tracker.as_mut().http_requests = self
202 .tracker
203 .as_ref()
204 .http_requests
205 .checked_add(1)
206 .ok_or(ArithmeticError::Overflow)?;
207 self.update_balance(self.policy.http_request)
208 }
209
210 pub(crate) fn track_fuel(&mut self, fuel: u64) -> Result<(), ExecutionError> {
212 self.tracker.as_mut().fuel = self
213 .tracker
214 .as_ref()
215 .fuel
216 .checked_add(fuel)
217 .ok_or(ArithmeticError::Overflow)?;
218 ensure!(
219 self.tracker.as_ref().fuel <= self.policy.maximum_fuel_per_block,
220 ExecutionError::MaximumFuelExceeded
221 );
222 self.update_balance(self.policy.fuel_price(fuel)?)
223 }
224
225 pub(crate) fn track_read_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
227 self.tracker.as_mut().read_operations = self
228 .tracker
229 .as_mut()
230 .read_operations
231 .checked_add(count)
232 .ok_or(ArithmeticError::Overflow)?;
233 self.update_balance(self.policy.read_operations_price(count)?)
234 }
235
236 pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
238 self.tracker.as_mut().write_operations = self
239 .tracker
240 .as_mut()
241 .write_operations
242 .checked_add(count)
243 .ok_or(ArithmeticError::Overflow)?;
244 self.update_balance(self.policy.write_operations_price(count)?)
245 }
246
247 pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
249 self.tracker.as_mut().bytes_read = self
250 .tracker
251 .as_mut()
252 .bytes_read
253 .checked_add(count)
254 .ok_or(ArithmeticError::Overflow)?;
255 if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
256 return Err(ExecutionError::ExcessiveRead);
257 }
258 self.update_balance(self.policy.bytes_read_price(count)?)?;
259 Ok(())
260 }
261
262 pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
264 self.tracker.as_mut().bytes_written = self
265 .tracker
266 .as_mut()
267 .bytes_written
268 .checked_add(count)
269 .ok_or(ArithmeticError::Overflow)?;
270 if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
271 return Err(ExecutionError::ExcessiveWrite);
272 }
273 self.update_balance(self.policy.bytes_written_price(count)?)?;
274 Ok(())
275 }
276
277 pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
279 {
280 let tracker = self.tracker.as_mut();
281 tracker.blob_bytes_read = tracker
282 .blob_bytes_read
283 .checked_add(count)
284 .ok_or(ArithmeticError::Overflow)?;
285 tracker.blobs_read = tracker
286 .blobs_read
287 .checked_add(1)
288 .ok_or(ArithmeticError::Overflow)?;
289 }
290 self.update_balance(self.policy.blob_read_price(count)?)?;
291 Ok(())
292 }
293
294 pub fn track_blob_published(&mut self, content: &BlobContent) -> Result<(), ExecutionError> {
296 self.policy.check_blob_size(content)?;
297 let size = content.bytes().len() as u64;
298 {
299 let tracker = self.tracker.as_mut();
300 tracker.blob_bytes_published = tracker
301 .blob_bytes_published
302 .checked_add(size)
303 .ok_or(ArithmeticError::Overflow)?;
304 tracker.blobs_published = tracker
305 .blobs_published
306 .checked_add(1)
307 .ok_or(ArithmeticError::Overflow)?;
308 }
309 self.update_balance(self.policy.blob_published_price(size)?)?;
310 Ok(())
311 }
312
313 #[allow(dead_code)]
316 pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
317 self.tracker.as_mut().bytes_stored = self
318 .tracker
319 .as_mut()
320 .bytes_stored
321 .checked_add(delta)
322 .ok_or(ArithmeticError::Overflow)?;
323 Ok(())
324 }
325
326 pub(crate) fn remaining_service_oracle_execution_time(
328 &self,
329 ) -> Result<Duration, ExecutionError> {
330 let tracker = self.tracker.as_ref();
331 let spent_execution_time = tracker.service_oracle_execution;
332 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
333
334 limit
335 .checked_sub(spent_execution_time)
336 .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
337 }
338
339 pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
341 self.tracker.as_mut().service_oracle_queries = self
342 .tracker
343 .as_mut()
344 .service_oracle_queries
345 .checked_add(1)
346 .ok_or(ArithmeticError::Overflow)?;
347 self.update_balance(self.policy.service_as_oracle_query)
348 }
349
350 pub(crate) fn track_service_oracle_execution(
352 &mut self,
353 execution_time: Duration,
354 ) -> Result<(), ExecutionError> {
355 let tracker = self.tracker.as_mut();
356 let spent_execution_time = &mut tracker.service_oracle_execution;
357 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
358
359 *spent_execution_time = spent_execution_time.saturating_add(execution_time);
360
361 ensure!(
362 *spent_execution_time < limit,
363 ExecutionError::MaximumServiceOracleExecutionTimeExceeded
364 );
365
366 Ok(())
367 }
368
369 pub(crate) fn track_service_oracle_response(
371 &mut self,
372 response_bytes: usize,
373 ) -> Result<(), ExecutionError> {
374 ensure!(
375 response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
376 ExecutionError::ServiceOracleResponseTooLarge
377 );
378
379 Ok(())
380 }
381}
382
383impl<Account, Tracker> ResourceController<Account, Tracker>
384where
385 Tracker: AsMut<ResourceTracker>,
386{
387 pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
389 self.track_block_size(bcs::serialized_size(data)?)
390 }
391
392 pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
394 let tracker = self.tracker.as_mut();
395 tracker.block_size = u64::try_from(size)
396 .ok()
397 .and_then(|size| tracker.block_size.checked_add(size))
398 .ok_or(ExecutionError::BlockTooLarge)?;
399 ensure!(
400 tracker.block_size <= self.policy.maximum_block_size,
401 ExecutionError::BlockTooLarge
402 );
403 Ok(())
404 }
405}
406
407impl BalanceHolder for Amount {
409 fn balance(&self) -> Result<Amount, ArithmeticError> {
410 Ok(*self)
411 }
412
413 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
414 self.try_add_assign(other)
415 }
416
417 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
418 self.try_sub_assign(other)
419 }
420}
421
422impl AsMut<ResourceTracker> for ResourceTracker {
425 fn as_mut(&mut self) -> &mut Self {
426 self
427 }
428}
429
430impl AsRef<ResourceTracker> for ResourceTracker {
431 fn as_ref(&self) -> &Self {
432 self
433 }
434}
435
436pub struct Sources<'a> {
438 sources: Vec<&'a mut Amount>,
439}
440
441impl BalanceHolder for Sources<'_> {
442 fn balance(&self) -> Result<Amount, ArithmeticError> {
443 let mut amount = Amount::ZERO;
444 for source in self.sources.iter() {
445 amount.try_add_assign(**source)?;
446 }
447 Ok(amount)
448 }
449
450 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
451 let source = self.sources.last_mut().expect("at least one source");
454 source.try_add_assign(other)
455 }
456
457 fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
458 for source in self.sources.iter_mut() {
459 if source.try_sub_assign(other).is_ok() {
460 return Ok(());
461 }
462 other.try_sub_assign(**source).expect("*source < other");
463 **source = Amount::ZERO;
464 }
465 if other > Amount::ZERO {
466 Err(ArithmeticError::Underflow)
467 } else {
468 Ok(())
469 }
470 }
471}
472
473impl ResourceController<Option<AccountOwner>, ResourceTracker> {
474 pub async fn with_state<'a, C>(
477 &mut self,
478 view: &'a mut SystemExecutionStateView<C>,
479 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
480 where
481 C: Context + Clone + Send + Sync + 'static,
482 {
483 self.with_state_and_grant(view, None).await
484 }
485
486 pub async fn with_state_and_grant<'a, C>(
490 &mut self,
491 view: &'a mut SystemExecutionStateView<C>,
492 grant: Option<&'a mut Amount>,
493 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
494 where
495 C: Context + Clone + Send + Sync + 'static,
496 {
497 let mut sources = Vec::new();
498 if let Some(grant) = grant {
501 sources.push(grant);
502 } else {
503 sources.push(view.balance.get_mut());
504 }
505 if let Some(owner) = &self.account {
508 if let Some(balance) = view.balances.get_mut(owner).await? {
509 sources.push(balance);
510 }
511 }
512
513 Ok(ResourceController {
514 policy: self.policy.clone(),
515 tracker: &mut self.tracker,
516 account: Sources { sources },
517 })
518 }
519}