Skip to main content

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