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