1use std::sync::Arc;
7
8use custom_debug_derive::Debug;
9use linera_base::{
10 data_types::{Amount, ArithmeticError},
11 ensure,
12 identifiers::Owner,
13};
14use linera_views::{context::Context, views::ViewError};
15use serde::Serialize;
16
17use crate::{
18 system::SystemExecutionError, ExecutionError, ExecutionStateView, Message, Operation,
19 ResourceControlPolicy,
20};
21
22#[derive(Clone, Debug, Default)]
23pub struct ResourceController<Account = Amount, Tracker = ResourceTracker> {
24 pub policy: Arc<ResourceControlPolicy>,
26 pub tracker: Tracker,
28 pub account: Account,
30}
31
32#[derive(Copy, Debug, Clone, Default)]
34pub struct ResourceTracker {
35 pub blocks: u32,
37 pub executed_block_size: u64,
39 pub fuel: u64,
41 pub read_operations: u32,
43 pub write_operations: u32,
45 pub bytes_read: u64,
47 pub bytes_written: u64,
49 pub bytes_stored: i32,
51 pub operations: u32,
53 pub operation_bytes: u64,
55 pub messages: u32,
57 pub message_bytes: u64,
59 pub grants: Amount,
61}
62
63pub trait BalanceHolder {
65 fn balance(&self) -> Result<Amount, ArithmeticError>;
66
67 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
68
69 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
70}
71
72impl<Account, Tracker> ResourceController<Account, Tracker>
74where
75 Account: BalanceHolder,
76 Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
77{
78 pub fn balance(&self) -> Result<Amount, ArithmeticError> {
81 self.account.balance()
82 }
83
84 pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
87 if other <= initial {
88 self.account
89 .try_sub_assign(initial.try_sub(other).expect("other <= initial"))
90 .map_err(|_| SystemExecutionError::InsufficientFundingForFees {
91 balance: self.balance().unwrap_or(Amount::MAX),
92 })?;
93 } else {
94 self.account
95 .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
96 }
97 Ok(())
98 }
99
100 fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
102 self.account.try_sub_assign(fees).map_err(|_| {
103 SystemExecutionError::InsufficientFundingForFees {
104 balance: self.balance().unwrap_or(Amount::MAX),
105 }
106 })?;
107 Ok(())
108 }
109
110 pub(crate) fn remaining_fuel(&self) -> u64 {
112 self.policy
113 .remaining_fuel(self.balance().unwrap_or(Amount::MAX))
114 .min(
115 self.policy
116 .maximum_fuel_per_block
117 .saturating_sub(self.tracker.as_ref().fuel),
118 )
119 }
120
121 pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
123 self.tracker.as_mut().grants.try_add_assign(grant)?;
124 self.update_balance(grant)
125 }
126
127 pub fn track_block(&mut self) -> Result<(), ExecutionError> {
129 self.tracker.as_mut().blocks = self
130 .tracker
131 .as_mut()
132 .blocks
133 .checked_add(1)
134 .ok_or(ArithmeticError::Overflow)?;
135 self.update_balance(self.policy.block)
136 }
137
138 pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
140 self.tracker.as_mut().operations = self
141 .tracker
142 .as_mut()
143 .operations
144 .checked_add(1)
145 .ok_or(ArithmeticError::Overflow)?;
146 self.update_balance(self.policy.operation)?;
147 match operation {
148 Operation::System(_) => Ok(()),
149 Operation::User { bytes, .. } => {
150 let size = bytes.len();
151 self.tracker.as_mut().operation_bytes = self
152 .tracker
153 .as_mut()
154 .operation_bytes
155 .checked_add(size as u64)
156 .ok_or(ArithmeticError::Overflow)?;
157 self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
158 Ok(())
159 }
160 }
161 }
162
163 pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
165 self.tracker.as_mut().messages = self
166 .tracker
167 .as_mut()
168 .messages
169 .checked_add(1)
170 .ok_or(ArithmeticError::Overflow)?;
171 self.update_balance(self.policy.message)?;
172 match message {
173 Message::System(_) => Ok(()),
174 Message::User { bytes, .. } => {
175 let size = bytes.len();
176 self.tracker.as_mut().message_bytes = self
177 .tracker
178 .as_mut()
179 .message_bytes
180 .checked_add(size as u64)
181 .ok_or(ArithmeticError::Overflow)?;
182 self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
183 Ok(())
184 }
185 }
186 }
187
188 pub(crate) fn track_fuel(&mut self, fuel: u64) -> Result<(), ExecutionError> {
190 self.tracker.as_mut().fuel = self
191 .tracker
192 .as_ref()
193 .fuel
194 .checked_add(fuel)
195 .ok_or(ArithmeticError::Overflow)?;
196 ensure!(
197 self.tracker.as_ref().fuel <= self.policy.maximum_fuel_per_block,
198 ExecutionError::MaximumFuelExceeded
199 );
200 self.update_balance(self.policy.fuel_price(fuel)?)
201 }
202
203 pub(crate) fn track_read_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
205 self.tracker.as_mut().read_operations = self
206 .tracker
207 .as_mut()
208 .read_operations
209 .checked_add(count)
210 .ok_or(ArithmeticError::Overflow)?;
211 self.update_balance(self.policy.read_operations_price(count)?)
212 }
213
214 pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
216 self.tracker.as_mut().write_operations = self
217 .tracker
218 .as_mut()
219 .write_operations
220 .checked_add(count)
221 .ok_or(ArithmeticError::Overflow)?;
222 self.update_balance(self.policy.write_operations_price(count)?)
223 }
224
225 pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
227 self.tracker.as_mut().bytes_read = self
228 .tracker
229 .as_mut()
230 .bytes_read
231 .checked_add(count)
232 .ok_or(ArithmeticError::Overflow)?;
233 if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
234 return Err(ExecutionError::ExcessiveRead);
235 }
236 self.update_balance(self.policy.bytes_read_price(count)?)?;
237 Ok(())
238 }
239
240 pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
242 self.tracker.as_mut().bytes_written = self
243 .tracker
244 .as_mut()
245 .bytes_written
246 .checked_add(count)
247 .ok_or(ArithmeticError::Overflow)?;
248 if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
249 return Err(ExecutionError::ExcessiveWrite);
250 }
251 self.update_balance(self.policy.bytes_written_price(count)?)?;
252 Ok(())
253 }
254
255 #[allow(dead_code)]
258 pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
259 self.tracker.as_mut().bytes_stored = self
260 .tracker
261 .as_mut()
262 .bytes_stored
263 .checked_add(delta)
264 .ok_or(ArithmeticError::Overflow)?;
265 Ok(())
266 }
267}
268
269impl<Account, Tracker> ResourceController<Account, Tracker>
270where
271 Tracker: AsMut<ResourceTracker>,
272{
273 pub fn track_executed_block_size_sequence_extension(
277 &mut self,
278 old_len: usize,
279 delta: usize,
280 ) -> Result<(), ExecutionError> {
281 if delta == 0 {
282 return Ok(());
283 }
284 let new_len = old_len + delta;
285 let old_size = ((usize::BITS - old_len.leading_zeros()) / 7).max(1);
287 let new_size = ((usize::BITS - new_len.leading_zeros()) / 7).max(1);
288 if new_size > old_size {
289 self.track_executed_block_size((new_size - old_size) as usize)?;
290 }
291 Ok(())
292 }
293
294 pub fn track_executed_block_size_of(
296 &mut self,
297 data: &impl Serialize,
298 ) -> Result<(), ExecutionError> {
299 self.track_executed_block_size(bcs::serialized_size(data)?)
300 }
301
302 pub fn track_executed_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
304 let tracker = self.tracker.as_mut();
305 tracker.executed_block_size = u64::try_from(size)
306 .ok()
307 .and_then(|size| tracker.executed_block_size.checked_add(size))
308 .ok_or(ExecutionError::ExecutedBlockTooLarge)?;
309 ensure!(
310 tracker.executed_block_size <= self.policy.maximum_executed_block_size,
311 ExecutionError::ExecutedBlockTooLarge
312 );
313 Ok(())
314 }
315}
316
317impl BalanceHolder for Amount {
319 fn balance(&self) -> Result<Amount, ArithmeticError> {
320 Ok(*self)
321 }
322
323 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
324 self.try_add_assign(other)
325 }
326
327 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
328 self.try_sub_assign(other)
329 }
330}
331
332impl AsMut<ResourceTracker> for ResourceTracker {
335 fn as_mut(&mut self) -> &mut Self {
336 self
337 }
338}
339
340impl AsRef<ResourceTracker> for ResourceTracker {
341 fn as_ref(&self) -> &Self {
342 self
343 }
344}
345
346pub struct Sources<'a> {
348 sources: Vec<&'a mut Amount>,
349}
350
351impl BalanceHolder for Sources<'_> {
352 fn balance(&self) -> Result<Amount, ArithmeticError> {
353 let mut amount = Amount::ZERO;
354 for source in self.sources.iter() {
355 amount.try_add_assign(**source)?;
356 }
357 Ok(amount)
358 }
359
360 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
361 let source = self.sources.last_mut().expect("at least one source");
364 source.try_add_assign(other)
365 }
366
367 fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
368 for source in self.sources.iter_mut() {
369 if source.try_sub_assign(other).is_ok() {
370 return Ok(());
371 }
372 other.try_sub_assign(**source).expect("*source < other");
373 **source = Amount::ZERO;
374 }
375 if other > Amount::ZERO {
376 Err(ArithmeticError::Underflow)
377 } else {
378 Ok(())
379 }
380 }
381}
382
383impl ResourceController<Option<Owner>, ResourceTracker> {
384 pub async fn with_state<'a, C>(
387 &mut self,
388 view: &'a mut ExecutionStateView<C>,
389 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
390 where
391 C: Context + Clone + Send + Sync + 'static,
392 {
393 self.with_state_and_grant(view, None).await
394 }
395
396 pub async fn with_state_and_grant<'a, C>(
400 &mut self,
401 view: &'a mut ExecutionStateView<C>,
402 grant: Option<&'a mut Amount>,
403 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
404 where
405 C: Context + Clone + Send + Sync + 'static,
406 {
407 let mut sources = Vec::new();
408 if let Some(grant) = grant {
411 sources.push(grant);
412 } else {
413 sources.push(view.system.balance.get_mut());
414 }
415 if let Some(owner) = &self.account {
418 if let Some(balance) = view.system.balances.get_mut(owner).await? {
419 sources.push(balance);
420 }
421 }
422
423 Ok(ResourceController {
424 policy: self.policy.clone(),
425 tracker: &mut self.tracker,
426 account: Sources { sources },
427 })
428 }
429}