1use std::{sync::Arc, time::Duration};
7
8use custom_debug_derive::Debug;
9use linera_base::{
10 data_types::{Amount, ArithmeticError, Blob},
11 ensure,
12 identifiers::AccountOwner,
13 vm::VmRuntime,
14};
15use linera_views::{context::Context, ViewError};
16use serde::Serialize;
17
18use crate::{ExecutionError, Message, Operation, ResourceControlPolicy, SystemExecutionStateView};
19
20#[derive(Clone, Debug, Default)]
21pub struct ResourceController<Account = Amount, Tracker = ResourceTracker> {
22 policy: Arc<ResourceControlPolicy>,
24 pub tracker: Tracker,
26 pub account: Account,
28}
29
30impl<Account, Tracker> ResourceController<Account, Tracker> {
31 pub fn new(policy: Arc<ResourceControlPolicy>, tracker: Tracker, account: Account) -> Self {
33 Self {
34 policy,
35 tracker,
36 account,
37 }
38 }
39
40 pub fn policy(&self) -> &Arc<ResourceControlPolicy> {
42 &self.policy
43 }
44
45 pub fn tracker(&self) -> &Tracker {
47 &self.tracker
48 }
49}
50
51#[derive(Copy, Debug, Clone, Default)]
57pub struct ResourceTracker {
58 pub block_size: u64,
60 pub evm_fuel: u64,
62 pub wasm_fuel: u64,
64 pub read_operations: u32,
66 pub write_operations: u32,
68 pub bytes_read: u64,
70 pub bytes_written: u64,
72 pub blobs_read: u32,
74 pub blobs_published: u32,
76 pub blob_bytes_read: u64,
78 pub blob_bytes_published: u64,
80 pub bytes_stored: i32,
82 pub operations: u32,
84 pub operation_bytes: u64,
86 pub messages: u32,
88 pub message_bytes: u64,
90 pub http_requests: u32,
92 pub service_oracle_queries: u32,
94 pub service_oracle_execution: Duration,
96 pub grants: Amount,
98}
99
100impl ResourceTracker {
101 fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
102 match vm_runtime {
103 VmRuntime::Wasm => self.wasm_fuel,
104 VmRuntime::Evm => self.evm_fuel,
105 }
106 }
107}
108
109pub trait BalanceHolder {
111 fn balance(&self) -> Result<Amount, ArithmeticError>;
112
113 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
114
115 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
116}
117
118impl<Account, Tracker> ResourceController<Account, Tracker>
120where
121 Account: BalanceHolder,
122 Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
123{
124 pub fn balance(&self) -> Result<Amount, ArithmeticError> {
127 self.account.balance()
128 }
129
130 pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
133 if other <= initial {
134 let sub_amount = initial.try_sub(other).expect("other <= initial");
135 self.account.try_sub_assign(sub_amount).map_err(|_| {
136 ExecutionError::FeesExceedFunding {
137 fees: sub_amount,
138 balance: self.balance().unwrap_or(Amount::MAX),
139 }
140 })?;
141 } else {
142 self.account
143 .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
144 }
145 Ok(())
146 }
147
148 fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
150 self.account
151 .try_sub_assign(fees)
152 .map_err(|_| ExecutionError::FeesExceedFunding {
153 fees,
154 balance: self.balance().unwrap_or(Amount::MAX),
155 })?;
156 Ok(())
157 }
158
159 pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
161 let balance = self.balance().unwrap_or(Amount::MAX);
162 let fuel = self.tracker.as_ref().fuel(vm_runtime);
163 let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
164 self.policy
165 .remaining_fuel(balance, vm_runtime)
166 .min(maximum_fuel_per_block.saturating_sub(fuel))
167 }
168
169 pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
171 self.tracker.as_mut().grants.try_add_assign(grant)?;
172 self.update_balance(grant)
173 }
174
175 pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
177 self.tracker.as_mut().operations = self
178 .tracker
179 .as_mut()
180 .operations
181 .checked_add(1)
182 .ok_or(ArithmeticError::Overflow)?;
183 self.update_balance(self.policy.operation)?;
184 match operation {
185 Operation::System(_) => Ok(()),
186 Operation::User { bytes, .. } => {
187 let size = bytes.len();
188 self.tracker.as_mut().operation_bytes = self
189 .tracker
190 .as_mut()
191 .operation_bytes
192 .checked_add(size as u64)
193 .ok_or(ArithmeticError::Overflow)?;
194 self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
195 Ok(())
196 }
197 }
198 }
199
200 pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
202 self.tracker.as_mut().messages = self
203 .tracker
204 .as_mut()
205 .messages
206 .checked_add(1)
207 .ok_or(ArithmeticError::Overflow)?;
208 self.update_balance(self.policy.message)?;
209 match message {
210 Message::System(_) => Ok(()),
211 Message::User { bytes, .. } => {
212 let size = bytes.len();
213 self.tracker.as_mut().message_bytes = self
214 .tracker
215 .as_mut()
216 .message_bytes
217 .checked_add(size as u64)
218 .ok_or(ArithmeticError::Overflow)?;
219 self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
220 Ok(())
221 }
222 }
223 }
224
225 pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
227 self.tracker.as_mut().http_requests = self
228 .tracker
229 .as_ref()
230 .http_requests
231 .checked_add(1)
232 .ok_or(ArithmeticError::Overflow)?;
233 self.update_balance(self.policy.http_request)
234 }
235
236 pub(crate) fn track_fuel(
238 &mut self,
239 fuel: u64,
240 vm_runtime: VmRuntime,
241 ) -> Result<(), ExecutionError> {
242 match vm_runtime {
243 VmRuntime::Wasm => {
244 self.tracker.as_mut().wasm_fuel = self
245 .tracker
246 .as_ref()
247 .wasm_fuel
248 .checked_add(fuel)
249 .ok_or(ArithmeticError::Overflow)?;
250 ensure!(
251 self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
252 ExecutionError::MaximumFuelExceeded(vm_runtime)
253 );
254 }
255 VmRuntime::Evm => {
256 self.tracker.as_mut().evm_fuel = self
257 .tracker
258 .as_ref()
259 .evm_fuel
260 .checked_add(fuel)
261 .ok_or(ArithmeticError::Overflow)?;
262 ensure!(
263 self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
264 ExecutionError::MaximumFuelExceeded(vm_runtime)
265 );
266 }
267 }
268 self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
269 }
270
271 pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
273 self.tracker.as_mut().read_operations = self
274 .tracker
275 .as_mut()
276 .read_operations
277 .checked_add(1)
278 .ok_or(ArithmeticError::Overflow)?;
279 self.update_balance(self.policy.read_operations_price(1)?)
280 }
281
282 pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
284 self.tracker.as_mut().write_operations = self
285 .tracker
286 .as_mut()
287 .write_operations
288 .checked_add(count)
289 .ok_or(ArithmeticError::Overflow)?;
290 self.update_balance(self.policy.write_operations_price(count)?)
291 }
292
293 pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
295 self.tracker.as_mut().bytes_read = self
296 .tracker
297 .as_mut()
298 .bytes_read
299 .checked_add(count)
300 .ok_or(ArithmeticError::Overflow)?;
301 if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
302 return Err(ExecutionError::ExcessiveRead);
303 }
304 self.update_balance(self.policy.bytes_read_price(count)?)?;
305 Ok(())
306 }
307
308 pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
310 self.tracker.as_mut().bytes_written = self
311 .tracker
312 .as_mut()
313 .bytes_written
314 .checked_add(count)
315 .ok_or(ArithmeticError::Overflow)?;
316 if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
317 return Err(ExecutionError::ExcessiveWrite);
318 }
319 self.update_balance(self.policy.bytes_written_price(count)?)?;
320 Ok(())
321 }
322
323 pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
325 {
326 let tracker = self.tracker.as_mut();
327 tracker.blob_bytes_read = tracker
328 .blob_bytes_read
329 .checked_add(count)
330 .ok_or(ArithmeticError::Overflow)?;
331 tracker.blobs_read = tracker
332 .blobs_read
333 .checked_add(1)
334 .ok_or(ArithmeticError::Overflow)?;
335 }
336 self.update_balance(self.policy.blob_read_price(count)?)?;
337 Ok(())
338 }
339
340 pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
342 self.policy.check_blob_size(blob.content())?;
343 let size = blob.content().bytes().len() as u64;
344 if blob.is_committee_blob() {
345 return Ok(());
346 }
347 {
348 let tracker = self.tracker.as_mut();
349 tracker.blob_bytes_published = tracker
350 .blob_bytes_published
351 .checked_add(size)
352 .ok_or(ArithmeticError::Overflow)?;
353 tracker.blobs_published = tracker
354 .blobs_published
355 .checked_add(1)
356 .ok_or(ArithmeticError::Overflow)?;
357 }
358 self.update_balance(self.policy.blob_published_price(size)?)?;
359 Ok(())
360 }
361
362 #[allow(dead_code)]
365 pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
366 self.tracker.as_mut().bytes_stored = self
367 .tracker
368 .as_mut()
369 .bytes_stored
370 .checked_add(delta)
371 .ok_or(ArithmeticError::Overflow)?;
372 Ok(())
373 }
374
375 pub(crate) fn remaining_service_oracle_execution_time(
377 &self,
378 ) -> Result<Duration, ExecutionError> {
379 let tracker = self.tracker.as_ref();
380 let spent_execution_time = tracker.service_oracle_execution;
381 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
382
383 limit
384 .checked_sub(spent_execution_time)
385 .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
386 }
387
388 pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
390 self.tracker.as_mut().service_oracle_queries = self
391 .tracker
392 .as_mut()
393 .service_oracle_queries
394 .checked_add(1)
395 .ok_or(ArithmeticError::Overflow)?;
396 self.update_balance(self.policy.service_as_oracle_query)
397 }
398
399 pub(crate) fn track_service_oracle_execution(
401 &mut self,
402 execution_time: Duration,
403 ) -> Result<(), ExecutionError> {
404 let tracker = self.tracker.as_mut();
405 let spent_execution_time = &mut tracker.service_oracle_execution;
406 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
407
408 *spent_execution_time = spent_execution_time.saturating_add(execution_time);
409
410 ensure!(
411 *spent_execution_time < limit,
412 ExecutionError::MaximumServiceOracleExecutionTimeExceeded
413 );
414
415 Ok(())
416 }
417
418 pub(crate) fn track_service_oracle_response(
420 &mut self,
421 response_bytes: usize,
422 ) -> Result<(), ExecutionError> {
423 ensure!(
424 response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
425 ExecutionError::ServiceOracleResponseTooLarge
426 );
427
428 Ok(())
429 }
430}
431
432impl<Account, Tracker> ResourceController<Account, Tracker>
433where
434 Tracker: AsMut<ResourceTracker>,
435{
436 pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
438 self.track_block_size(bcs::serialized_size(data)?)
439 }
440
441 pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
443 let tracker = self.tracker.as_mut();
444 tracker.block_size = u64::try_from(size)
445 .ok()
446 .and_then(|size| tracker.block_size.checked_add(size))
447 .ok_or(ExecutionError::BlockTooLarge)?;
448 ensure!(
449 tracker.block_size <= self.policy.maximum_block_size,
450 ExecutionError::BlockTooLarge
451 );
452 Ok(())
453 }
454}
455
456impl ResourceController<Option<AccountOwner>, ResourceTracker> {
457 pub async fn with_state<'a, C>(
460 &mut self,
461 view: &'a mut SystemExecutionStateView<C>,
462 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
463 where
464 C: Context + Clone + Send + Sync + 'static,
465 {
466 self.with_state_and_grant(view, None).await
467 }
468
469 pub async fn with_state_and_grant<'a, C>(
473 &mut self,
474 view: &'a mut SystemExecutionStateView<C>,
475 grant: Option<&'a mut Amount>,
476 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
477 where
478 C: Context + Clone + Send + Sync + 'static,
479 {
480 let mut sources = Vec::new();
481 if let Some(grant) = grant {
484 sources.push(grant);
485 } else {
486 sources.push(view.balance.get_mut());
487 }
488 if let Some(owner) = &self.account {
491 if let Some(balance) = view.balances.get_mut(owner).await? {
492 sources.push(balance);
493 }
494 }
495
496 Ok(ResourceController {
497 policy: self.policy.clone(),
498 tracker: &mut self.tracker,
499 account: Sources { sources },
500 })
501 }
502}
503
504impl BalanceHolder for Amount {
506 fn balance(&self) -> Result<Amount, ArithmeticError> {
507 Ok(*self)
508 }
509
510 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
511 self.try_add_assign(other)
512 }
513
514 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
515 self.try_sub_assign(other)
516 }
517}
518
519impl AsMut<ResourceTracker> for ResourceTracker {
522 fn as_mut(&mut self) -> &mut Self {
523 self
524 }
525}
526
527impl AsRef<ResourceTracker> for ResourceTracker {
528 fn as_ref(&self) -> &Self {
529 self
530 }
531}
532
533pub struct Sources<'a> {
535 sources: Vec<&'a mut Amount>,
536}
537
538impl BalanceHolder for Sources<'_> {
539 fn balance(&self) -> Result<Amount, ArithmeticError> {
540 let mut amount = Amount::ZERO;
541 for source in self.sources.iter() {
542 amount.try_add_assign(**source)?;
543 }
544 Ok(amount)
545 }
546
547 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
548 let source = self.sources.last_mut().expect("at least one source");
551 source.try_add_assign(other)
552 }
553
554 fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
555 for source in self.sources.iter_mut() {
556 if source.try_sub_assign(other).is_ok() {
557 return Ok(());
558 }
559 other.try_sub_assign(**source).expect("*source < other");
560 **source = Amount::ZERO;
561 }
562 if other > Amount::ZERO {
563 Err(ArithmeticError::Underflow)
564 } else {
565 Ok(())
566 }
567 }
568}