1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! This module tracks the resources used during the execution of a transaction.

use crate::{policy::ResourceControlPolicy, system::SystemExecutionError, ExecutionError};

use custom_debug_derive::Debug;
use linera_base::data_types::Amount;

/// The entries of the runtime related to storage
#[derive(Copy, Debug, Clone)]
pub struct RuntimeLimits {
    /// The maximum read requests per block
    pub max_budget_num_reads: u64,
    /// The maximum number of bytes that can be read per block
    pub max_budget_bytes_read: u64,
    /// The maximum number of bytes that can be written per block
    pub max_budget_bytes_written: u64,
    /// The maximum size of read allowed per block
    pub maximum_bytes_left_to_read: u64,
    /// The maximum size of write allowed per block
    pub maximum_bytes_left_to_write: u64,
}

/// The entries of the runtime related to storage
#[derive(Copy, Debug, Clone)]
pub struct ResourceTracker {
    /// The used fuel in the computation
    pub used_fuel: u64,
    /// The number of reads in the computation
    pub num_reads: u64,
    /// The total number of bytes read
    pub bytes_read: u64,
    /// The total number of bytes written
    pub bytes_written: u64,
    /// The maximum size of read that remains available for use
    pub maximum_bytes_left_to_read: u64,
    /// The maximum size of write that remains available for use
    pub maximum_bytes_left_to_write: u64,
}

#[cfg(any(test, feature = "test"))]
impl Default for ResourceTracker {
    fn default() -> Self {
        ResourceTracker {
            used_fuel: 0,
            num_reads: 0,
            bytes_read: 0,
            bytes_written: 0,
            maximum_bytes_left_to_read: u64::MAX / 2,
            maximum_bytes_left_to_write: u64::MAX / 2,
        }
    }
}

impl Default for RuntimeLimits {
    fn default() -> Self {
        RuntimeLimits {
            max_budget_num_reads: u64::MAX / 2,
            max_budget_bytes_read: u64::MAX / 2,
            max_budget_bytes_written: u64::MAX / 2,
            maximum_bytes_left_to_read: u64::MAX / 2,
            maximum_bytes_left_to_write: u64::MAX / 2,
        }
    }
}

impl ResourceTracker {
    /// Subtracts an amount from a balance and reports an error if that is impossible
    fn sub_assign_fees(balance: &mut Amount, fees: Amount) -> Result<(), SystemExecutionError> {
        balance
            .try_sub_assign(fees)
            .map_err(|_| SystemExecutionError::InsufficientFunding {
                current_balance: *balance,
            })
    }

    /// Updates the limits for the maximum and updates the balance.
    pub fn update_limits(
        &mut self,
        balance: &mut Amount,
        policy: &ResourceControlPolicy,
        runtime_counts: RuntimeCounts,
    ) -> Result<(), ExecutionError> {
        // The fuel being used
        let initial_fuel = policy.remaining_fuel(*balance);
        let used_fuel = initial_fuel.saturating_sub(runtime_counts.remaining_fuel);
        self.used_fuel += used_fuel;
        Self::sub_assign_fees(balance, policy.fuel_price(used_fuel)?)?;
        // The number of reads
        Self::sub_assign_fees(
            balance,
            policy.storage_num_reads_price(&runtime_counts.num_reads)?,
        )?;
        self.num_reads += runtime_counts.num_reads;
        // The number of bytes read
        let bytes_read = runtime_counts.bytes_read;
        self.maximum_bytes_left_to_read -= bytes_read;
        self.bytes_read += runtime_counts.bytes_read;
        Self::sub_assign_fees(balance, policy.storage_bytes_read_price(&bytes_read)?)?;
        // The number of bytes written
        let bytes_written = runtime_counts.bytes_written;
        self.maximum_bytes_left_to_write -= bytes_written;
        self.bytes_written += bytes_written;
        Self::sub_assign_fees(balance, policy.storage_bytes_written_price(&bytes_written)?)?;
        Ok(())
    }

    /// Obtain the limits for the running of the system
    pub fn limits(&self, policy: &ResourceControlPolicy, balance: &Amount) -> RuntimeLimits {
        let max_budget_num_reads =
            u64::try_from(balance.saturating_div(policy.storage_num_reads)).unwrap_or(u64::MAX);
        let max_budget_bytes_read =
            u64::try_from(balance.saturating_div(policy.storage_bytes_read)).unwrap_or(u64::MAX);
        let max_budget_bytes_written =
            u64::try_from(balance.saturating_div(policy.storage_bytes_read)).unwrap_or(u64::MAX);
        RuntimeLimits {
            max_budget_num_reads,
            max_budget_bytes_read,
            max_budget_bytes_written,
            maximum_bytes_left_to_read: self.maximum_bytes_left_to_read,
            maximum_bytes_left_to_write: self.maximum_bytes_left_to_write,
        }
    }
}

/// The entries of the runtime related to fuel and storage
#[derive(Copy, Debug, Clone)]
pub struct RuntimeCounts {
    /// The remaining fuel available
    pub remaining_fuel: u64,
    /// The number of read operations
    pub num_reads: u64,
    /// The bytes that have been read
    pub bytes_read: u64,
    /// The bytes that have been written
    pub bytes_written: u64,
}