linera_execution/
resources.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module tracks the resources used during the execution of a transaction.
5
6use std::{fmt, sync::Arc, time::Duration};
7
8use custom_debug_derive::Debug;
9use linera_base::{
10    data_types::{Amount, ApplicationDescription, 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    /// The (fixed) policy used to charge fees and control resource usage.
24    policy: Arc<ResourceControlPolicy>,
25    /// How the resources were used so far.
26    pub tracker: Tracker,
27    /// The account paying for the resource usage.
28    pub account: Account,
29    /// When true, balance deductions are skipped (fees waived for free apps).
30    pub is_free: bool,
31}
32
33impl<Account, Tracker> ResourceController<Account, Tracker> {
34    /// Creates a new resource controller with the given policy and account.
35    pub fn new(policy: Arc<ResourceControlPolicy>, tracker: Tracker, account: Account) -> Self {
36        Self {
37            policy,
38            tracker,
39            account,
40            is_free: false,
41        }
42    }
43
44    /// Returns a reference to the policy.
45    pub fn policy(&self) -> &Arc<ResourceControlPolicy> {
46        &self.policy
47    }
48
49    /// Returns a reference to the tracker.
50    pub fn tracker(&self) -> &Tracker {
51        &self.tracker
52    }
53}
54
55/// The runtime size of an `Amount`.
56pub const RUNTIME_AMOUNT_SIZE: u32 = 16;
57
58/// The runtime size of a `ApplicationId`.
59pub const RUNTIME_APPLICATION_ID_SIZE: u32 = 32;
60
61/// The runtime size of a `BlockHeight`.
62pub const RUNTIME_BLOCK_HEIGHT_SIZE: u32 = 8;
63
64/// The runtime size of a `ChainId`.
65pub const RUNTIME_CHAIN_ID_SIZE: u32 = 32;
66
67/// The runtime size of a `Timestamp`.
68pub const RUNTIME_TIMESTAMP_SIZE: u32 = 8;
69
70/// The runtime size of the weight of an owner.
71pub const RUNTIME_OWNER_WEIGHT_SIZE: u32 = 8;
72
73/// The runtime constant part size of the `ChainOwnership`.
74/// It consists of one `u32` and four `TimeDelta` which are the constant part of
75/// the `ChainOwnership`. The way we do it is not optimal:
76/// TODO(#4164): Implement a procedure for computing naive sizes.
77pub const RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE: u32 = 4 + 4 * 8;
78
79/// The runtime size of a `CryptoHash`.
80pub const RUNTIME_CRYPTO_HASH_SIZE: u32 = 32;
81
82/// The runtime size of a `VmRuntime` enum.
83pub const RUNTIME_VM_RUNTIME_SIZE: u32 = 1;
84
85/// The runtime constant part size of an `ApplicationDescription`.
86/// This includes: `ModuleId` (2 hashes + VmRuntime) + `ChainId` + `BlockHeight` + `u32`.
87/// Variable parts (`parameters` and `required_application_ids`) are calculated separately.
88pub const RUNTIME_CONSTANT_APPLICATION_DESCRIPTION_SIZE: u32 = 2 * RUNTIME_CRYPTO_HASH_SIZE + RUNTIME_VM_RUNTIME_SIZE  // ModuleId
89    + RUNTIME_CHAIN_ID_SIZE                                  // creator_chain_id
90    + RUNTIME_BLOCK_HEIGHT_SIZE                              // block_height
91    + 4; // application_index (u32)
92
93#[cfg(test)]
94mod tests {
95    use std::mem::size_of;
96
97    use linera_base::{
98        data_types::{Amount, ApplicationDescription, BlockHeight, Timestamp},
99        identifiers::{ApplicationId, ChainId, ModuleId},
100    };
101
102    use crate::resources::{
103        RUNTIME_AMOUNT_SIZE, RUNTIME_APPLICATION_ID_SIZE, RUNTIME_BLOCK_HEIGHT_SIZE,
104        RUNTIME_CHAIN_ID_SIZE, RUNTIME_CONSTANT_APPLICATION_DESCRIPTION_SIZE,
105        RUNTIME_OWNER_WEIGHT_SIZE, RUNTIME_TIMESTAMP_SIZE,
106    };
107
108    #[test]
109    fn test_size_of_runtime_operations() {
110        assert_eq!(RUNTIME_AMOUNT_SIZE as usize, size_of::<Amount>());
111        assert_eq!(
112            RUNTIME_APPLICATION_ID_SIZE as usize,
113            size_of::<ApplicationId>()
114        );
115        assert_eq!(RUNTIME_BLOCK_HEIGHT_SIZE as usize, size_of::<BlockHeight>());
116        assert_eq!(RUNTIME_CHAIN_ID_SIZE as usize, size_of::<ChainId>());
117        assert_eq!(RUNTIME_TIMESTAMP_SIZE as usize, size_of::<Timestamp>());
118        assert_eq!(RUNTIME_OWNER_WEIGHT_SIZE as usize, size_of::<u64>());
119    }
120
121    /// Verifies that `RUNTIME_CONSTANT_APPLICATION_DESCRIPTION_SIZE` matches the actual
122    /// structure of `ApplicationDescription`. This test will fail if a new fixed-size
123    /// field is added to the struct.
124    #[test]
125    fn test_application_description_size() {
126        // Verify using BCS serialization, which is architecture-independent.
127        // BCS encodes Vec length as ULEB128, so empty vectors add 1 byte each.
128        let description = ApplicationDescription {
129            module_id: ModuleId::default(),
130            creator_chain_id: ChainId::default(),
131            block_height: BlockHeight::default(),
132            application_index: 0,
133            parameters: vec![],
134            required_application_ids: vec![],
135        };
136        let serialized = bcs::to_bytes(&description).expect("serialization should succeed");
137        // Serialized size = fixed fields + 2 bytes for empty vectors (1 byte each for ULEB128 length).
138        assert_eq!(
139            serialized.len(),
140            RUNTIME_CONSTANT_APPLICATION_DESCRIPTION_SIZE as usize + 2
141        );
142    }
143}
144
145/// The resources used so far by an execution process.
146/// Acts as an accumulator for all resources consumed during
147/// a specific execution flow. This could be the execution of a block,
148/// the processing of a single message, or a specific phase within these
149/// broader operations.
150#[derive(Copy, Debug, Clone, Default)]
151pub struct ResourceTracker {
152    /// The total size of the block so far.
153    pub block_size: u64,
154    /// The EVM fuel used so far.
155    pub evm_fuel: u64,
156    /// The Wasm fuel used so far.
157    pub wasm_fuel: u64,
158    /// The number of read operations.
159    pub read_operations: u32,
160    /// The number of write operations.
161    pub write_operations: u32,
162    /// The size of bytes read from runtime.
163    pub bytes_runtime: u32,
164    /// The number of bytes read.
165    pub bytes_read: u64,
166    /// The number of bytes written.
167    pub bytes_written: u64,
168    /// The number of blobs read.
169    pub blobs_read: u32,
170    /// The number of blobs published.
171    pub blobs_published: u32,
172    /// The number of blob bytes read.
173    pub blob_bytes_read: u64,
174    /// The number of blob bytes published.
175    pub blob_bytes_published: u64,
176    /// The number of events read.
177    pub events_read: u32,
178    /// The number of events published.
179    pub events_published: u32,
180    /// The number of event bytes read.
181    pub event_bytes_read: u64,
182    /// The number of event bytes published.
183    pub event_bytes_published: u64,
184    /// The change in the number of bytes being stored by user applications.
185    pub bytes_stored: i32,
186    /// The number of operations executed.
187    pub operations: u32,
188    /// The total size of the arguments of user operations.
189    pub operation_bytes: u64,
190    /// The number of outgoing messages created (system and user).
191    pub messages: u32,
192    /// The total size of the arguments of outgoing user messages.
193    pub message_bytes: u64,
194    /// The number of HTTP requests performed.
195    pub http_requests: u32,
196    /// The number of calls to services as oracles.
197    pub service_oracle_queries: u32,
198    /// The time spent executing services as oracles.
199    pub service_oracle_execution: Duration,
200    /// The amount allocated to message grants.
201    pub grants: Amount,
202}
203
204impl ResourceTracker {
205    fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
206        match vm_runtime {
207            VmRuntime::Wasm => self.wasm_fuel,
208            VmRuntime::Evm => self.evm_fuel,
209        }
210    }
211}
212
213impl fmt::Display for ResourceTracker {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        let mut lines = Vec::new();
216
217        let mut block_parts = Vec::new();
218        if self.block_size != 0 {
219            block_parts.push(format!("size={}", self.block_size));
220        }
221        if self.operations != 0 {
222            block_parts.push(format!("operations={}", self.operations));
223        }
224        if self.operation_bytes != 0 {
225            block_parts.push(format!("operation_bytes={}", self.operation_bytes));
226        }
227        if !block_parts.is_empty() {
228            lines.push(format!("block: {}", block_parts.join(", ")));
229        }
230
231        let mut fuel_parts = Vec::new();
232        if self.wasm_fuel != 0 {
233            fuel_parts.push(format!("wasm={}", self.wasm_fuel));
234        }
235        if self.evm_fuel != 0 {
236            fuel_parts.push(format!("evm={}", self.evm_fuel));
237        }
238        if !fuel_parts.is_empty() {
239            lines.push(format!("fuel: {}", fuel_parts.join(", ")));
240        }
241
242        let mut storage_parts = Vec::new();
243        if self.read_operations != 0 {
244            storage_parts.push(format!("reads={}", self.read_operations));
245        }
246        if self.write_operations != 0 {
247            storage_parts.push(format!("writes={}", self.write_operations));
248        }
249        if self.bytes_runtime != 0 {
250            storage_parts.push(format!("runtime_bytes={}", self.bytes_runtime));
251        }
252        if self.bytes_read != 0 {
253            storage_parts.push(format!("bytes_read={}", self.bytes_read));
254        }
255        if self.bytes_written != 0 {
256            storage_parts.push(format!("bytes_written={}", self.bytes_written));
257        }
258        if self.bytes_stored != 0 {
259            storage_parts.push(format!("bytes_stored={}", self.bytes_stored));
260        }
261        if !storage_parts.is_empty() {
262            lines.push(format!("storage: {}", storage_parts.join(", ")));
263        }
264
265        let mut blob_parts = Vec::new();
266        if self.blobs_read != 0 {
267            blob_parts.push(format!("read={}", self.blobs_read));
268        }
269        if self.blobs_published != 0 {
270            blob_parts.push(format!("published={}", self.blobs_published));
271        }
272        if self.blob_bytes_read != 0 {
273            blob_parts.push(format!("bytes_read={}", self.blob_bytes_read));
274        }
275        if self.blob_bytes_published != 0 {
276            blob_parts.push(format!("bytes_published={}", self.blob_bytes_published));
277        }
278        if !blob_parts.is_empty() {
279            lines.push(format!("blobs: {}", blob_parts.join(", ")));
280        }
281
282        let mut event_parts = Vec::new();
283        if self.events_read != 0 {
284            event_parts.push(format!("read={}", self.events_read));
285        }
286        if self.events_published != 0 {
287            event_parts.push(format!("published={}", self.events_published));
288        }
289        if self.event_bytes_read != 0 {
290            event_parts.push(format!("bytes_read={}", self.event_bytes_read));
291        }
292        if self.event_bytes_published != 0 {
293            event_parts.push(format!("bytes_published={}", self.event_bytes_published));
294        }
295        if !event_parts.is_empty() {
296            lines.push(format!("events: {}", event_parts.join(", ")));
297        }
298
299        let mut message_parts = Vec::new();
300        if self.messages != 0 {
301            message_parts.push(format!("count={}", self.messages));
302        }
303        if self.message_bytes != 0 {
304            message_parts.push(format!("bytes={}", self.message_bytes));
305        }
306        if self.grants != Amount::ZERO {
307            message_parts.push(format!("grants={}", self.grants));
308        }
309        if !message_parts.is_empty() {
310            lines.push(format!("messages: {}", message_parts.join(", ")));
311        }
312
313        let mut http_service_parts = Vec::new();
314        if self.http_requests != 0 {
315            http_service_parts.push(format!("http_requests={}", self.http_requests));
316        }
317        if self.service_oracle_queries != 0 {
318            http_service_parts.push(format!("service_queries={}", self.service_oracle_queries));
319        }
320        if self.service_oracle_execution != Duration::ZERO {
321            http_service_parts.push(format!(
322                "service_execution={:?}",
323                self.service_oracle_execution
324            ));
325        }
326        if !http_service_parts.is_empty() {
327            lines.push(format!("http/service: {}", http_service_parts.join(", ")));
328        }
329
330        let mut lines_iter = lines.into_iter();
331        if let Some(first) = lines_iter.next() {
332            write!(f, "{}", first)?;
333            for line in lines_iter {
334                write!(f, "\n  {}", line)?;
335            }
336        }
337
338        Ok(())
339    }
340}
341
342/// How to access the balance of an account.
343pub trait BalanceHolder {
344    fn balance(&self) -> Result<Amount, ArithmeticError>;
345
346    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
347
348    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
349}
350
351// The main accounting functions for a ResourceController.
352impl<Account, Tracker> ResourceController<Account, Tracker>
353where
354    Account: BalanceHolder,
355    Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
356{
357    /// Obtains the balance of the account. The only possible error is an arithmetic
358    /// overflow, which should not happen in practice due to final token supply.
359    pub fn balance(&self) -> Result<Amount, ArithmeticError> {
360        self.account.balance()
361    }
362
363    /// Operates a 3-way merge by transferring the difference between `initial`
364    /// and `other` to `self`.
365    pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
366        if other <= initial {
367            let sub_amount = initial.try_sub(other).expect("other <= initial");
368            self.account.try_sub_assign(sub_amount).map_err(|_| {
369                ExecutionError::FeesExceedFunding {
370                    fees: sub_amount,
371                    balance: self.balance().unwrap_or(Amount::MAX),
372                }
373            })?;
374        } else {
375            self.account
376                .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
377        }
378        Ok(())
379    }
380
381    /// Subtracts an amount from a balance and reports an error if that is impossible.
382    /// When `is_free` is set, balance deductions are skipped (fees waived).
383    fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
384        if self.is_free {
385            return Ok(());
386        }
387        self.account
388            .try_sub_assign(fees)
389            .map_err(|_| ExecutionError::FeesExceedFunding {
390                fees,
391                balance: self.balance().unwrap_or(Amount::MAX),
392            })?;
393        Ok(())
394    }
395
396    /// Obtains the amount of fuel that could be spent by consuming the entire balance.
397    pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
398        let fuel = self.tracker.as_ref().fuel(vm_runtime);
399        let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
400        if self.is_free {
401            return maximum_fuel_per_block.saturating_sub(fuel);
402        }
403        let balance = self.balance().unwrap_or(Amount::MAX);
404        self.policy
405            .remaining_fuel(balance, vm_runtime)
406            .min(maximum_fuel_per_block.saturating_sub(fuel))
407    }
408
409    /// Tracks the allocation of a grant.
410    pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
411        self.tracker.as_mut().grants.try_add_assign(grant)?;
412        self.update_balance(grant)
413    }
414
415    /// Tracks the execution of an operation in block.
416    pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
417        self.tracker.as_mut().operations = self
418            .tracker
419            .as_mut()
420            .operations
421            .checked_add(1)
422            .ok_or(ArithmeticError::Overflow)?;
423        self.update_balance(self.policy.operation)?;
424        match operation {
425            Operation::System(_) => Ok(()),
426            Operation::User { bytes, .. } => {
427                let size = bytes.len();
428                self.tracker.as_mut().operation_bytes = self
429                    .tracker
430                    .as_mut()
431                    .operation_bytes
432                    .checked_add(size as u64)
433                    .ok_or(ArithmeticError::Overflow)?;
434                self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
435                Ok(())
436            }
437        }
438    }
439
440    /// Tracks the creation of an outgoing message.
441    pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
442        self.tracker.as_mut().messages = self
443            .tracker
444            .as_mut()
445            .messages
446            .checked_add(1)
447            .ok_or(ArithmeticError::Overflow)?;
448        self.update_balance(self.policy.message)?;
449        match message {
450            Message::System(_) => Ok(()),
451            Message::User { bytes, .. } => {
452                let size = bytes.len();
453                self.tracker.as_mut().message_bytes = self
454                    .tracker
455                    .as_mut()
456                    .message_bytes
457                    .checked_add(size as u64)
458                    .ok_or(ArithmeticError::Overflow)?;
459                self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
460                Ok(())
461            }
462        }
463    }
464
465    /// Tracks the execution of an HTTP request.
466    pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
467        self.tracker.as_mut().http_requests = self
468            .tracker
469            .as_ref()
470            .http_requests
471            .checked_add(1)
472            .ok_or(ArithmeticError::Overflow)?;
473        self.update_balance(self.policy.http_request)
474    }
475
476    /// Tracks a number of fuel units used.
477    pub(crate) fn track_fuel(
478        &mut self,
479        fuel: u64,
480        vm_runtime: VmRuntime,
481    ) -> Result<(), ExecutionError> {
482        match vm_runtime {
483            VmRuntime::Wasm => {
484                self.tracker.as_mut().wasm_fuel = self
485                    .tracker
486                    .as_ref()
487                    .wasm_fuel
488                    .checked_add(fuel)
489                    .ok_or(ArithmeticError::Overflow)?;
490                ensure!(
491                    self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
492                    ExecutionError::MaximumFuelExceeded(vm_runtime)
493                );
494            }
495            VmRuntime::Evm => {
496                self.tracker.as_mut().evm_fuel = self
497                    .tracker
498                    .as_ref()
499                    .evm_fuel
500                    .checked_add(fuel)
501                    .ok_or(ArithmeticError::Overflow)?;
502                ensure!(
503                    self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
504                    ExecutionError::MaximumFuelExceeded(vm_runtime)
505                );
506            }
507        }
508        self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
509    }
510
511    /// Tracks runtime reading of `ChainId`
512    pub(crate) fn track_runtime_chain_id(&mut self) -> Result<(), ExecutionError> {
513        self.track_size_runtime_operations(RUNTIME_CHAIN_ID_SIZE)
514    }
515
516    /// Tracks runtime reading of `BlockHeight`
517    pub(crate) fn track_runtime_block_height(&mut self) -> Result<(), ExecutionError> {
518        self.track_size_runtime_operations(RUNTIME_BLOCK_HEIGHT_SIZE)
519    }
520
521    /// Tracks runtime reading of `ApplicationId`
522    pub(crate) fn track_runtime_application_id(&mut self) -> Result<(), ExecutionError> {
523        self.track_size_runtime_operations(RUNTIME_APPLICATION_ID_SIZE)
524    }
525
526    /// Tracks runtime reading of application parameters.
527    pub(crate) fn track_runtime_application_parameters(
528        &mut self,
529        parameters: &[u8],
530    ) -> Result<(), ExecutionError> {
531        let parameters_len = parameters.len() as u32;
532        self.track_size_runtime_operations(parameters_len)
533    }
534
535    /// Tracks runtime reading of `Timestamp`
536    pub(crate) fn track_runtime_timestamp(&mut self) -> Result<(), ExecutionError> {
537        self.track_size_runtime_operations(RUNTIME_TIMESTAMP_SIZE)
538    }
539
540    /// Tracks runtime reading of balance
541    pub(crate) fn track_runtime_balance(&mut self) -> Result<(), ExecutionError> {
542        self.track_size_runtime_operations(RUNTIME_AMOUNT_SIZE)
543    }
544
545    /// Tracks runtime reading of owner balances
546    pub(crate) fn track_runtime_owner_balances(
547        &mut self,
548        owner_balances: &[(AccountOwner, Amount)],
549    ) -> Result<(), ExecutionError> {
550        let mut size = 0;
551        for (account_owner, _) in owner_balances {
552            size += account_owner.size() + RUNTIME_AMOUNT_SIZE;
553        }
554        self.track_size_runtime_operations(size)
555    }
556
557    /// Tracks runtime reading of owners
558    pub(crate) fn track_runtime_owners(
559        &mut self,
560        owners: &[AccountOwner],
561    ) -> Result<(), ExecutionError> {
562        let mut size = 0;
563        for owner in owners {
564            size += owner.size();
565        }
566        self.track_size_runtime_operations(size)
567    }
568
569    /// Tracks runtime reading of owners
570    pub(crate) fn track_runtime_chain_ownership(
571        &mut self,
572        chain_ownership: &ChainOwnership,
573    ) -> Result<(), ExecutionError> {
574        let mut size = 0;
575        for account_owner in &chain_ownership.super_owners {
576            size += account_owner.size();
577        }
578        for account_owner in chain_ownership.owners.keys() {
579            size += account_owner.size() + RUNTIME_OWNER_WEIGHT_SIZE;
580        }
581        size += RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE;
582        self.track_size_runtime_operations(size)
583    }
584
585    /// Tracks runtime reading of an application description.
586    pub(crate) fn track_runtime_application_description(
587        &mut self,
588        description: &ApplicationDescription,
589    ) -> Result<(), ExecutionError> {
590        let parameters_size = description.parameters.len() as u32;
591        let required_apps_size =
592            description.required_application_ids.len() as u32 * RUNTIME_APPLICATION_ID_SIZE;
593        let size =
594            RUNTIME_CONSTANT_APPLICATION_DESCRIPTION_SIZE + parameters_size + required_apps_size;
595        self.track_size_runtime_operations(size)
596    }
597
598    /// Tracks runtime operations.
599    fn track_size_runtime_operations(&mut self, size: u32) -> Result<(), ExecutionError> {
600        self.tracker.as_mut().bytes_runtime = self
601            .tracker
602            .as_mut()
603            .bytes_runtime
604            .checked_add(size)
605            .ok_or(ArithmeticError::Overflow)?;
606        self.update_balance(self.policy.bytes_runtime_price(size)?)
607    }
608
609    /// Tracks a read operation.
610    pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
611        self.tracker.as_mut().read_operations = self
612            .tracker
613            .as_mut()
614            .read_operations
615            .checked_add(1)
616            .ok_or(ArithmeticError::Overflow)?;
617        self.update_balance(self.policy.read_operations_price(1)?)
618    }
619
620    /// Tracks a write operation.
621    pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
622        self.tracker.as_mut().write_operations = self
623            .tracker
624            .as_mut()
625            .write_operations
626            .checked_add(count)
627            .ok_or(ArithmeticError::Overflow)?;
628        self.update_balance(self.policy.write_operations_price(count)?)
629    }
630
631    /// Tracks a number of bytes read.
632    pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
633        self.tracker.as_mut().bytes_read = self
634            .tracker
635            .as_mut()
636            .bytes_read
637            .checked_add(count)
638            .ok_or(ArithmeticError::Overflow)?;
639        if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
640            return Err(ExecutionError::ExcessiveRead);
641        }
642        self.update_balance(self.policy.bytes_read_price(count)?)?;
643        Ok(())
644    }
645
646    /// Tracks a number of bytes written.
647    pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
648        self.tracker.as_mut().bytes_written = self
649            .tracker
650            .as_mut()
651            .bytes_written
652            .checked_add(count)
653            .ok_or(ArithmeticError::Overflow)?;
654        if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
655            return Err(ExecutionError::ExcessiveWrite);
656        }
657        self.update_balance(self.policy.bytes_written_price(count)?)?;
658        Ok(())
659    }
660
661    /// Tracks a number of blob bytes written.
662    pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
663        {
664            let tracker = self.tracker.as_mut();
665            tracker.blob_bytes_read = tracker
666                .blob_bytes_read
667                .checked_add(count)
668                .ok_or(ArithmeticError::Overflow)?;
669            tracker.blobs_read = tracker
670                .blobs_read
671                .checked_add(1)
672                .ok_or(ArithmeticError::Overflow)?;
673        }
674        self.update_balance(self.policy.blob_read_price(count)?)?;
675        Ok(())
676    }
677
678    /// Tracks a number of blob bytes published.
679    pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
680        self.policy.check_blob_size(blob.content())?;
681        let size = blob.content().bytes().len() as u64;
682        if blob.is_committee_blob() {
683            return Ok(());
684        }
685        {
686            let tracker = self.tracker.as_mut();
687            tracker.blob_bytes_published = tracker
688                .blob_bytes_published
689                .checked_add(size)
690                .ok_or(ArithmeticError::Overflow)?;
691            tracker.blobs_published = tracker
692                .blobs_published
693                .checked_add(1)
694                .ok_or(ArithmeticError::Overflow)?;
695        }
696        self.update_balance(self.policy.blob_published_price(size)?)?;
697        Ok(())
698    }
699
700    /// Tracks a number of event bytes read.
701    pub(crate) fn track_event_read(&mut self, count: u64) -> Result<(), ExecutionError> {
702        {
703            let tracker = self.tracker.as_mut();
704            tracker.event_bytes_read = tracker
705                .event_bytes_read
706                .checked_add(count)
707                .ok_or(ArithmeticError::Overflow)?;
708            tracker.events_read = tracker
709                .events_read
710                .checked_add(1)
711                .ok_or(ArithmeticError::Overflow)?;
712        }
713        self.update_balance(self.policy.blob_read_price(count)?)?;
714        Ok(())
715    }
716
717    /// Tracks a number of event bytes published.
718    pub(crate) fn track_event_published(
719        &mut self,
720        event_bytes: &[u8],
721    ) -> Result<(), ExecutionError> {
722        let size = event_bytes.len() as u64;
723        {
724            let tracker = self.tracker.as_mut();
725            tracker.event_bytes_published = tracker
726                .event_bytes_published
727                .checked_add(size)
728                .ok_or(ArithmeticError::Overflow)?;
729            tracker.events_published = tracker
730                .events_published
731                .checked_add(1)
732                .ok_or(ArithmeticError::Overflow)?;
733        }
734        self.update_balance(self.policy.blob_published_price(size)?)?;
735        Ok(())
736    }
737
738    /// Tracks a change in the number of bytes stored.
739    // TODO(#1536): This is not fully implemented.
740    #[allow(dead_code)]
741    pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
742        self.tracker.as_mut().bytes_stored = self
743            .tracker
744            .as_mut()
745            .bytes_stored
746            .checked_add(delta)
747            .ok_or(ArithmeticError::Overflow)?;
748        Ok(())
749    }
750
751    /// Returns the remaining time services can spend executing as oracles.
752    pub(crate) fn remaining_service_oracle_execution_time(
753        &self,
754    ) -> Result<Duration, ExecutionError> {
755        let tracker = self.tracker.as_ref();
756        let spent_execution_time = tracker.service_oracle_execution;
757        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
758
759        limit
760            .checked_sub(spent_execution_time)
761            .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
762    }
763
764    /// Tracks a call to a service to run as an oracle.
765    pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
766        self.tracker.as_mut().service_oracle_queries = self
767            .tracker
768            .as_mut()
769            .service_oracle_queries
770            .checked_add(1)
771            .ok_or(ArithmeticError::Overflow)?;
772        self.update_balance(self.policy.service_as_oracle_query)
773    }
774
775    /// Tracks the time spent executing the service as an oracle.
776    pub(crate) fn track_service_oracle_execution(
777        &mut self,
778        execution_time: Duration,
779    ) -> Result<(), ExecutionError> {
780        let tracker = self.tracker.as_mut();
781        let spent_execution_time = &mut tracker.service_oracle_execution;
782        let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
783
784        *spent_execution_time = spent_execution_time.saturating_add(execution_time);
785
786        ensure!(
787            *spent_execution_time < limit,
788            ExecutionError::MaximumServiceOracleExecutionTimeExceeded
789        );
790
791        Ok(())
792    }
793
794    /// Tracks the size of a response produced by an oracle.
795    pub(crate) fn track_service_oracle_response(
796        &self,
797        response_bytes: usize,
798    ) -> Result<(), ExecutionError> {
799        ensure!(
800            response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
801            ExecutionError::ServiceOracleResponseTooLarge
802        );
803
804        Ok(())
805    }
806}
807
808impl<Account, Tracker> ResourceController<Account, Tracker>
809where
810    Tracker: AsMut<ResourceTracker>,
811{
812    /// Tracks the serialized size of a block, or parts of it.
813    pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
814        self.track_block_size(bcs::serialized_size(data)?)
815    }
816
817    /// Tracks the serialized size of a block, or parts of it.
818    pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
819        let tracker = self.tracker.as_mut();
820        tracker.block_size = u64::try_from(size)
821            .ok()
822            .and_then(|size| tracker.block_size.checked_add(size))
823            .ok_or(ExecutionError::BlockTooLarge)?;
824        ensure!(
825            tracker.block_size <= self.policy.maximum_block_size,
826            ExecutionError::BlockTooLarge
827        );
828        Ok(())
829    }
830}
831
832impl ResourceController<Option<AccountOwner>, ResourceTracker> {
833    /// Provides a reference to the current execution state and obtains a temporary object
834    /// where the accounting functions of [`ResourceController`] are available.
835    pub async fn with_state<'a, C>(
836        &mut self,
837        view: &'a mut SystemExecutionStateView<C>,
838    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
839    where
840        C: Context + Clone + 'static,
841    {
842        self.with_state_and_grant(view, None).await
843    }
844
845    /// Provides a reference to the current execution state as well as an optional grant,
846    /// and obtains a temporary object where the accounting functions of
847    /// [`ResourceController`] are available.
848    pub async fn with_state_and_grant<'a, C>(
849        &mut self,
850        view: &'a mut SystemExecutionStateView<C>,
851        grant: Option<&'a mut Amount>,
852    ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
853    where
854        C: Context + Clone + 'static,
855    {
856        let mut sources = Vec::new();
857        // First, use the grant (e.g. for messages) and otherwise use the chain account
858        // (e.g. for blocks and operations).
859        if let Some(grant) = grant {
860            sources.push(grant);
861        } else {
862            sources.push(view.balance.get_mut());
863        }
864        // Then the local account, if any. Currently, any negative fee (e.g. storage
865        // refund) goes preferably to this account.
866        if let Some(owner) = &self.account {
867            if let Some(balance) = view.balances.get_mut(owner).await? {
868                sources.push(balance);
869            }
870        }
871
872        Ok(ResourceController {
873            policy: self.policy.clone(),
874            tracker: &mut self.tracker,
875            account: Sources { sources },
876            is_free: self.is_free,
877        })
878    }
879}
880
881// The simplest `BalanceHolder` is an `Amount`.
882impl BalanceHolder for Amount {
883    fn balance(&self) -> Result<Amount, ArithmeticError> {
884        Ok(*self)
885    }
886
887    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
888        self.try_add_assign(other)
889    }
890
891    fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
892        self.try_sub_assign(other)
893    }
894}
895
896// This is also needed for the default instantiation `ResourceController<Amount, ResourceTracker>`.
897// See https://doc.rust-lang.org/std/convert/trait.AsMut.html#reflexivity for general context.
898impl AsMut<ResourceTracker> for ResourceTracker {
899    fn as_mut(&mut self) -> &mut Self {
900        self
901    }
902}
903
904impl AsRef<ResourceTracker> for ResourceTracker {
905    fn as_ref(&self) -> &Self {
906        self
907    }
908}
909
910/// A temporary object holding a number of references to funding sources.
911pub struct Sources<'a> {
912    sources: Vec<&'a mut Amount>,
913}
914
915impl BalanceHolder for Sources<'_> {
916    fn balance(&self) -> Result<Amount, ArithmeticError> {
917        let mut amount = Amount::ZERO;
918        for source in &self.sources {
919            amount.try_add_assign(**source)?;
920        }
921        Ok(amount)
922    }
923
924    fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
925        // Try to credit the owner account first.
926        // TODO(#1648): This may need some additional design work.
927        let source = self.sources.last_mut().expect("at least one source");
928        source.try_add_assign(other)
929    }
930
931    fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
932        for source in &mut self.sources {
933            if source.try_sub_assign(other).is_ok() {
934                return Ok(());
935            }
936            other.try_sub_assign(**source).expect("*source < other");
937            **source = Amount::ZERO;
938        }
939        if other > Amount::ZERO {
940            Err(ArithmeticError::Underflow)
941        } else {
942            Ok(())
943        }
944    }
945}