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 ownership::ChainOwnership,
14 vm::VmRuntime,
15};
16use linera_views::{context::Context, ViewError};
17use serde::Serialize;
18
19use crate::{ExecutionError, Message, Operation, ResourceControlPolicy, SystemExecutionStateView};
20
21#[derive(Clone, Debug, Default)]
22pub struct ResourceController<Account = Amount, Tracker = ResourceTracker> {
23 policy: Arc<ResourceControlPolicy>,
25 pub tracker: Tracker,
27 pub account: Account,
29}
30
31impl<Account, Tracker> ResourceController<Account, Tracker> {
32 pub fn new(policy: Arc<ResourceControlPolicy>, tracker: Tracker, account: Account) -> Self {
34 Self {
35 policy,
36 tracker,
37 account,
38 }
39 }
40
41 pub fn policy(&self) -> &Arc<ResourceControlPolicy> {
43 &self.policy
44 }
45
46 pub fn tracker(&self) -> &Tracker {
48 &self.tracker
49 }
50}
51
52pub const RUNTIME_AMOUNT_SIZE: u32 = 16;
54
55pub const RUNTIME_APPLICATION_ID_SIZE: u32 = 32;
57
58pub const RUNTIME_BLOCK_HEIGHT_SIZE: u32 = 8;
60
61pub const RUNTIME_CHAIN_ID_SIZE: u32 = 32;
63
64pub const RUNTIME_TIMESTAMP_SIZE: u32 = 8;
66
67pub const RUNTIME_OWNER_WEIGHT_SIZE: u32 = 8;
69
70pub const RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE: u32 = 4 + 4 * 8;
75
76#[cfg(test)]
77mod tests {
78 use std::mem::size_of;
79
80 use linera_base::{
81 data_types::{Amount, BlockHeight, Timestamp},
82 identifiers::{ApplicationId, ChainId},
83 };
84
85 use crate::resources::{
86 RUNTIME_AMOUNT_SIZE, RUNTIME_APPLICATION_ID_SIZE, RUNTIME_BLOCK_HEIGHT_SIZE,
87 RUNTIME_CHAIN_ID_SIZE, RUNTIME_OWNER_WEIGHT_SIZE, RUNTIME_TIMESTAMP_SIZE,
88 };
89
90 #[test]
91 fn test_size_of_runtime_operations() {
92 assert_eq!(RUNTIME_AMOUNT_SIZE as usize, size_of::<Amount>());
93 assert_eq!(
94 RUNTIME_APPLICATION_ID_SIZE as usize,
95 size_of::<ApplicationId>()
96 );
97 assert_eq!(RUNTIME_BLOCK_HEIGHT_SIZE as usize, size_of::<BlockHeight>());
98 assert_eq!(RUNTIME_CHAIN_ID_SIZE as usize, size_of::<ChainId>());
99 assert_eq!(RUNTIME_TIMESTAMP_SIZE as usize, size_of::<Timestamp>());
100 assert_eq!(RUNTIME_OWNER_WEIGHT_SIZE as usize, size_of::<u64>());
101 }
102}
103
104#[derive(Copy, Debug, Clone, Default)]
110pub struct ResourceTracker {
111 pub block_size: u64,
113 pub evm_fuel: u64,
115 pub wasm_fuel: u64,
117 pub read_operations: u32,
119 pub write_operations: u32,
121 pub bytes_runtime: u32,
123 pub bytes_read: u64,
125 pub bytes_written: u64,
127 pub blobs_read: u32,
129 pub blobs_published: u32,
131 pub blob_bytes_read: u64,
133 pub blob_bytes_published: u64,
135 pub events_read: u32,
137 pub events_published: u32,
139 pub event_bytes_read: u64,
141 pub event_bytes_published: u64,
143 pub bytes_stored: i32,
145 pub operations: u32,
147 pub operation_bytes: u64,
149 pub messages: u32,
151 pub message_bytes: u64,
153 pub http_requests: u32,
155 pub service_oracle_queries: u32,
157 pub service_oracle_execution: Duration,
159 pub grants: Amount,
161}
162
163impl ResourceTracker {
164 fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
165 match vm_runtime {
166 VmRuntime::Wasm => self.wasm_fuel,
167 VmRuntime::Evm => self.evm_fuel,
168 }
169 }
170}
171
172pub trait BalanceHolder {
174 fn balance(&self) -> Result<Amount, ArithmeticError>;
175
176 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
177
178 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
179}
180
181impl<Account, Tracker> ResourceController<Account, Tracker>
183where
184 Account: BalanceHolder,
185 Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
186{
187 pub fn balance(&self) -> Result<Amount, ArithmeticError> {
190 self.account.balance()
191 }
192
193 pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
196 if other <= initial {
197 let sub_amount = initial.try_sub(other).expect("other <= initial");
198 self.account.try_sub_assign(sub_amount).map_err(|_| {
199 ExecutionError::FeesExceedFunding {
200 fees: sub_amount,
201 balance: self.balance().unwrap_or(Amount::MAX),
202 }
203 })?;
204 } else {
205 self.account
206 .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
207 }
208 Ok(())
209 }
210
211 fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
213 self.account
214 .try_sub_assign(fees)
215 .map_err(|_| ExecutionError::FeesExceedFunding {
216 fees,
217 balance: self.balance().unwrap_or(Amount::MAX),
218 })?;
219 Ok(())
220 }
221
222 pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
224 let balance = self.balance().unwrap_or(Amount::MAX);
225 let fuel = self.tracker.as_ref().fuel(vm_runtime);
226 let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
227 self.policy
228 .remaining_fuel(balance, vm_runtime)
229 .min(maximum_fuel_per_block.saturating_sub(fuel))
230 }
231
232 pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
234 self.tracker.as_mut().grants.try_add_assign(grant)?;
235 self.update_balance(grant)
236 }
237
238 pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
240 self.tracker.as_mut().operations = self
241 .tracker
242 .as_mut()
243 .operations
244 .checked_add(1)
245 .ok_or(ArithmeticError::Overflow)?;
246 self.update_balance(self.policy.operation)?;
247 match operation {
248 Operation::System(_) => Ok(()),
249 Operation::User { bytes, .. } => {
250 let size = bytes.len();
251 self.tracker.as_mut().operation_bytes = self
252 .tracker
253 .as_mut()
254 .operation_bytes
255 .checked_add(size as u64)
256 .ok_or(ArithmeticError::Overflow)?;
257 self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
258 Ok(())
259 }
260 }
261 }
262
263 pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
265 self.tracker.as_mut().messages = self
266 .tracker
267 .as_mut()
268 .messages
269 .checked_add(1)
270 .ok_or(ArithmeticError::Overflow)?;
271 self.update_balance(self.policy.message)?;
272 match message {
273 Message::System(_) => Ok(()),
274 Message::User { bytes, .. } => {
275 let size = bytes.len();
276 self.tracker.as_mut().message_bytes = self
277 .tracker
278 .as_mut()
279 .message_bytes
280 .checked_add(size as u64)
281 .ok_or(ArithmeticError::Overflow)?;
282 self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
283 Ok(())
284 }
285 }
286 }
287
288 pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
290 self.tracker.as_mut().http_requests = self
291 .tracker
292 .as_ref()
293 .http_requests
294 .checked_add(1)
295 .ok_or(ArithmeticError::Overflow)?;
296 self.update_balance(self.policy.http_request)
297 }
298
299 pub(crate) fn track_fuel(
301 &mut self,
302 fuel: u64,
303 vm_runtime: VmRuntime,
304 ) -> Result<(), ExecutionError> {
305 match vm_runtime {
306 VmRuntime::Wasm => {
307 self.tracker.as_mut().wasm_fuel = self
308 .tracker
309 .as_ref()
310 .wasm_fuel
311 .checked_add(fuel)
312 .ok_or(ArithmeticError::Overflow)?;
313 ensure!(
314 self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
315 ExecutionError::MaximumFuelExceeded(vm_runtime)
316 );
317 }
318 VmRuntime::Evm => {
319 self.tracker.as_mut().evm_fuel = self
320 .tracker
321 .as_ref()
322 .evm_fuel
323 .checked_add(fuel)
324 .ok_or(ArithmeticError::Overflow)?;
325 ensure!(
326 self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
327 ExecutionError::MaximumFuelExceeded(vm_runtime)
328 );
329 }
330 }
331 self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
332 }
333
334 pub(crate) fn track_runtime_chain_id(&mut self) -> Result<(), ExecutionError> {
336 self.track_size_runtime_operations(RUNTIME_CHAIN_ID_SIZE)
337 }
338
339 pub(crate) fn track_runtime_block_height(&mut self) -> Result<(), ExecutionError> {
341 self.track_size_runtime_operations(RUNTIME_BLOCK_HEIGHT_SIZE)
342 }
343
344 pub(crate) fn track_runtime_application_id(&mut self) -> Result<(), ExecutionError> {
346 self.track_size_runtime_operations(RUNTIME_APPLICATION_ID_SIZE)
347 }
348
349 pub(crate) fn track_runtime_application_parameters(
351 &mut self,
352 parameters: &[u8],
353 ) -> Result<(), ExecutionError> {
354 let parameters_len = parameters.len() as u32;
355 self.track_size_runtime_operations(parameters_len)
356 }
357
358 pub(crate) fn track_runtime_timestamp(&mut self) -> Result<(), ExecutionError> {
360 self.track_size_runtime_operations(RUNTIME_TIMESTAMP_SIZE)
361 }
362
363 pub(crate) fn track_runtime_balance(&mut self) -> Result<(), ExecutionError> {
365 self.track_size_runtime_operations(RUNTIME_AMOUNT_SIZE)
366 }
367
368 pub(crate) fn track_runtime_owner_balances(
370 &mut self,
371 owner_balances: &[(AccountOwner, Amount)],
372 ) -> Result<(), ExecutionError> {
373 let mut size = 0;
374 for (account_owner, _) in owner_balances {
375 size += account_owner.size() + RUNTIME_AMOUNT_SIZE;
376 }
377 self.track_size_runtime_operations(size)
378 }
379
380 pub(crate) fn track_runtime_owners(
382 &mut self,
383 owners: &[AccountOwner],
384 ) -> Result<(), ExecutionError> {
385 let mut size = 0;
386 for owner in owners {
387 size += owner.size();
388 }
389 self.track_size_runtime_operations(size)
390 }
391
392 pub(crate) fn track_runtime_chain_ownership(
394 &mut self,
395 chain_ownership: &ChainOwnership,
396 ) -> Result<(), ExecutionError> {
397 let mut size = 0;
398 for account_owner in &chain_ownership.super_owners {
399 size += account_owner.size();
400 }
401 for account_owner in chain_ownership.owners.keys() {
402 size += account_owner.size() + RUNTIME_OWNER_WEIGHT_SIZE;
403 }
404 size += RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE;
405 self.track_size_runtime_operations(size)
406 }
407
408 fn track_size_runtime_operations(&mut self, size: u32) -> Result<(), ExecutionError> {
410 self.tracker.as_mut().bytes_runtime = self
411 .tracker
412 .as_mut()
413 .bytes_runtime
414 .checked_add(size)
415 .ok_or(ArithmeticError::Overflow)?;
416 self.update_balance(self.policy.bytes_runtime_price(size)?)
417 }
418
419 pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
421 self.tracker.as_mut().read_operations = self
422 .tracker
423 .as_mut()
424 .read_operations
425 .checked_add(1)
426 .ok_or(ArithmeticError::Overflow)?;
427 self.update_balance(self.policy.read_operations_price(1)?)
428 }
429
430 pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
432 self.tracker.as_mut().write_operations = self
433 .tracker
434 .as_mut()
435 .write_operations
436 .checked_add(count)
437 .ok_or(ArithmeticError::Overflow)?;
438 self.update_balance(self.policy.write_operations_price(count)?)
439 }
440
441 pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
443 self.tracker.as_mut().bytes_read = self
444 .tracker
445 .as_mut()
446 .bytes_read
447 .checked_add(count)
448 .ok_or(ArithmeticError::Overflow)?;
449 if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
450 return Err(ExecutionError::ExcessiveRead);
451 }
452 self.update_balance(self.policy.bytes_read_price(count)?)?;
453 Ok(())
454 }
455
456 pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
458 self.tracker.as_mut().bytes_written = self
459 .tracker
460 .as_mut()
461 .bytes_written
462 .checked_add(count)
463 .ok_or(ArithmeticError::Overflow)?;
464 if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
465 return Err(ExecutionError::ExcessiveWrite);
466 }
467 self.update_balance(self.policy.bytes_written_price(count)?)?;
468 Ok(())
469 }
470
471 pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
473 {
474 let tracker = self.tracker.as_mut();
475 tracker.blob_bytes_read = tracker
476 .blob_bytes_read
477 .checked_add(count)
478 .ok_or(ArithmeticError::Overflow)?;
479 tracker.blobs_read = tracker
480 .blobs_read
481 .checked_add(1)
482 .ok_or(ArithmeticError::Overflow)?;
483 }
484 self.update_balance(self.policy.blob_read_price(count)?)?;
485 Ok(())
486 }
487
488 pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
490 self.policy.check_blob_size(blob.content())?;
491 let size = blob.content().bytes().len() as u64;
492 if blob.is_committee_blob() {
493 return Ok(());
494 }
495 {
496 let tracker = self.tracker.as_mut();
497 tracker.blob_bytes_published = tracker
498 .blob_bytes_published
499 .checked_add(size)
500 .ok_or(ArithmeticError::Overflow)?;
501 tracker.blobs_published = tracker
502 .blobs_published
503 .checked_add(1)
504 .ok_or(ArithmeticError::Overflow)?;
505 }
506 self.update_balance(self.policy.blob_published_price(size)?)?;
507 Ok(())
508 }
509
510 pub(crate) fn track_event_read(&mut self, count: u64) -> Result<(), ExecutionError> {
512 {
513 let tracker = self.tracker.as_mut();
514 tracker.event_bytes_read = tracker
515 .event_bytes_read
516 .checked_add(count)
517 .ok_or(ArithmeticError::Overflow)?;
518 tracker.events_read = tracker
519 .events_read
520 .checked_add(1)
521 .ok_or(ArithmeticError::Overflow)?;
522 }
523 self.update_balance(self.policy.blob_read_price(count)?)?;
524 Ok(())
525 }
526
527 pub(crate) fn track_event_published(
529 &mut self,
530 event_bytes: &[u8],
531 ) -> Result<(), ExecutionError> {
532 let size = event_bytes.len() as u64;
533 {
534 let tracker = self.tracker.as_mut();
535 tracker.event_bytes_published = tracker
536 .event_bytes_published
537 .checked_add(size)
538 .ok_or(ArithmeticError::Overflow)?;
539 tracker.events_published = tracker
540 .events_published
541 .checked_add(1)
542 .ok_or(ArithmeticError::Overflow)?;
543 }
544 self.update_balance(self.policy.blob_published_price(size)?)?;
545 Ok(())
546 }
547
548 #[allow(dead_code)]
551 pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
552 self.tracker.as_mut().bytes_stored = self
553 .tracker
554 .as_mut()
555 .bytes_stored
556 .checked_add(delta)
557 .ok_or(ArithmeticError::Overflow)?;
558 Ok(())
559 }
560
561 pub(crate) fn remaining_service_oracle_execution_time(
563 &self,
564 ) -> Result<Duration, ExecutionError> {
565 let tracker = self.tracker.as_ref();
566 let spent_execution_time = tracker.service_oracle_execution;
567 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
568
569 limit
570 .checked_sub(spent_execution_time)
571 .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
572 }
573
574 pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
576 self.tracker.as_mut().service_oracle_queries = self
577 .tracker
578 .as_mut()
579 .service_oracle_queries
580 .checked_add(1)
581 .ok_or(ArithmeticError::Overflow)?;
582 self.update_balance(self.policy.service_as_oracle_query)
583 }
584
585 pub(crate) fn track_service_oracle_execution(
587 &mut self,
588 execution_time: Duration,
589 ) -> Result<(), ExecutionError> {
590 let tracker = self.tracker.as_mut();
591 let spent_execution_time = &mut tracker.service_oracle_execution;
592 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
593
594 *spent_execution_time = spent_execution_time.saturating_add(execution_time);
595
596 ensure!(
597 *spent_execution_time < limit,
598 ExecutionError::MaximumServiceOracleExecutionTimeExceeded
599 );
600
601 Ok(())
602 }
603
604 pub(crate) fn track_service_oracle_response(
606 &mut self,
607 response_bytes: usize,
608 ) -> Result<(), ExecutionError> {
609 ensure!(
610 response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
611 ExecutionError::ServiceOracleResponseTooLarge
612 );
613
614 Ok(())
615 }
616}
617
618impl<Account, Tracker> ResourceController<Account, Tracker>
619where
620 Tracker: AsMut<ResourceTracker>,
621{
622 pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
624 self.track_block_size(bcs::serialized_size(data)?)
625 }
626
627 pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
629 let tracker = self.tracker.as_mut();
630 tracker.block_size = u64::try_from(size)
631 .ok()
632 .and_then(|size| tracker.block_size.checked_add(size))
633 .ok_or(ExecutionError::BlockTooLarge)?;
634 ensure!(
635 tracker.block_size <= self.policy.maximum_block_size,
636 ExecutionError::BlockTooLarge
637 );
638 Ok(())
639 }
640}
641
642impl ResourceController<Option<AccountOwner>, ResourceTracker> {
643 pub async fn with_state<'a, C>(
646 &mut self,
647 view: &'a mut SystemExecutionStateView<C>,
648 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
649 where
650 C: Context + Clone + Send + Sync + 'static,
651 {
652 self.with_state_and_grant(view, None).await
653 }
654
655 pub async fn with_state_and_grant<'a, C>(
659 &mut self,
660 view: &'a mut SystemExecutionStateView<C>,
661 grant: Option<&'a mut Amount>,
662 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
663 where
664 C: Context + Clone + Send + Sync + 'static,
665 {
666 let mut sources = Vec::new();
667 if let Some(grant) = grant {
670 sources.push(grant);
671 } else {
672 sources.push(view.balance.get_mut());
673 }
674 if let Some(owner) = &self.account {
677 if let Some(balance) = view.balances.get_mut(owner).await? {
678 sources.push(balance);
679 }
680 }
681
682 Ok(ResourceController {
683 policy: self.policy.clone(),
684 tracker: &mut self.tracker,
685 account: Sources { sources },
686 })
687 }
688}
689
690impl BalanceHolder for Amount {
692 fn balance(&self) -> Result<Amount, ArithmeticError> {
693 Ok(*self)
694 }
695
696 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
697 self.try_add_assign(other)
698 }
699
700 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
701 self.try_sub_assign(other)
702 }
703}
704
705impl AsMut<ResourceTracker> for ResourceTracker {
708 fn as_mut(&mut self) -> &mut Self {
709 self
710 }
711}
712
713impl AsRef<ResourceTracker> for ResourceTracker {
714 fn as_ref(&self) -> &Self {
715 self
716 }
717}
718
719pub struct Sources<'a> {
721 sources: Vec<&'a mut Amount>,
722}
723
724impl BalanceHolder for Sources<'_> {
725 fn balance(&self) -> Result<Amount, ArithmeticError> {
726 let mut amount = Amount::ZERO;
727 for source in &self.sources {
728 amount.try_add_assign(**source)?;
729 }
730 Ok(amount)
731 }
732
733 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
734 let source = self.sources.last_mut().expect("at least one source");
737 source.try_add_assign(other)
738 }
739
740 fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
741 for source in &mut self.sources {
742 if source.try_sub_assign(other).is_ok() {
743 return Ok(());
744 }
745 other.try_sub_assign(**source).expect("*source < other");
746 **source = Amount::ZERO;
747 }
748 if other > Amount::ZERO {
749 Err(ArithmeticError::Underflow)
750 } else {
751 Ok(())
752 }
753 }
754}