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