1use std::{sync::Arc, time::Duration};
7
8use custom_debug_derive::Debug;
9use linera_base::{
10 data_types::{Amount, 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 policy: Arc<ResourceControlPolicy>,
25 pub tracker: Tracker,
27 pub account: Account,
29}
30
31impl<Account, Tracker> ResourceController<Account, Tracker> {
32 pub fn new(policy: Arc<ResourceControlPolicy>, tracker: Tracker, account: Account) -> Self {
34 Self {
35 policy,
36 tracker,
37 account,
38 }
39 }
40
41 pub fn policy(&self) -> &Arc<ResourceControlPolicy> {
43 &self.policy
44 }
45
46 pub fn tracker(&self) -> &Tracker {
48 &self.tracker
49 }
50}
51
52pub const RUNTIME_AMOUNT_SIZE: u32 = 16;
54
55pub const RUNTIME_APPLICATION_ID_SIZE: u32 = 32;
57
58pub const RUNTIME_BLOCK_HEIGHT_SIZE: u32 = 8;
60
61pub const RUNTIME_CHAIN_ID_SIZE: u32 = 32;
63
64pub const RUNTIME_TIMESTAMP_SIZE: u32 = 8;
66
67pub const RUNTIME_OWNER_WEIGHT_SIZE: u32 = 8;
69
70pub const RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE: u32 = 4 + 4 * 8;
75
76#[cfg(test)]
77mod tests {
78 use std::mem::size_of;
79
80 use linera_base::{
81 data_types::{Amount, BlockHeight, Timestamp},
82 identifiers::{ApplicationId, ChainId},
83 };
84
85 use crate::resources::{
86 RUNTIME_AMOUNT_SIZE, RUNTIME_APPLICATION_ID_SIZE, RUNTIME_BLOCK_HEIGHT_SIZE,
87 RUNTIME_CHAIN_ID_SIZE, RUNTIME_OWNER_WEIGHT_SIZE, RUNTIME_TIMESTAMP_SIZE,
88 };
89
90 #[test]
91 fn test_size_of_runtime_operations() {
92 assert_eq!(RUNTIME_AMOUNT_SIZE as usize, size_of::<Amount>());
93 assert_eq!(
94 RUNTIME_APPLICATION_ID_SIZE as usize,
95 size_of::<ApplicationId>()
96 );
97 assert_eq!(RUNTIME_BLOCK_HEIGHT_SIZE as usize, size_of::<BlockHeight>());
98 assert_eq!(RUNTIME_CHAIN_ID_SIZE as usize, size_of::<ChainId>());
99 assert_eq!(RUNTIME_TIMESTAMP_SIZE as usize, size_of::<Timestamp>());
100 assert_eq!(RUNTIME_OWNER_WEIGHT_SIZE as usize, size_of::<u64>());
101 }
102}
103
104#[derive(Copy, Debug, Clone, Default)]
110pub struct ResourceTracker {
111 pub block_size: u64,
113 pub evm_fuel: u64,
115 pub wasm_fuel: u64,
117 pub read_operations: u32,
119 pub write_operations: u32,
121 pub bytes_runtime: u32,
123 pub bytes_read: u64,
125 pub bytes_written: u64,
127 pub blobs_read: u32,
129 pub blobs_published: u32,
131 pub blob_bytes_read: u64,
133 pub blob_bytes_published: u64,
135 pub bytes_stored: i32,
137 pub operations: u32,
139 pub operation_bytes: u64,
141 pub messages: u32,
143 pub message_bytes: u64,
145 pub http_requests: u32,
147 pub service_oracle_queries: u32,
149 pub service_oracle_execution: Duration,
151 pub grants: Amount,
153}
154
155impl ResourceTracker {
156 fn fuel(&self, vm_runtime: VmRuntime) -> u64 {
157 match vm_runtime {
158 VmRuntime::Wasm => self.wasm_fuel,
159 VmRuntime::Evm => self.evm_fuel,
160 }
161 }
162}
163
164pub trait BalanceHolder {
166 fn balance(&self) -> Result<Amount, ArithmeticError>;
167
168 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
169
170 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError>;
171}
172
173impl<Account, Tracker> ResourceController<Account, Tracker>
175where
176 Account: BalanceHolder,
177 Tracker: AsRef<ResourceTracker> + AsMut<ResourceTracker>,
178{
179 pub fn balance(&self) -> Result<Amount, ArithmeticError> {
182 self.account.balance()
183 }
184
185 pub fn merge_balance(&mut self, initial: Amount, other: Amount) -> Result<(), ExecutionError> {
188 if other <= initial {
189 let sub_amount = initial.try_sub(other).expect("other <= initial");
190 self.account.try_sub_assign(sub_amount).map_err(|_| {
191 ExecutionError::FeesExceedFunding {
192 fees: sub_amount,
193 balance: self.balance().unwrap_or(Amount::MAX),
194 }
195 })?;
196 } else {
197 self.account
198 .try_add_assign(other.try_sub(initial).expect("other > initial"))?;
199 }
200 Ok(())
201 }
202
203 fn update_balance(&mut self, fees: Amount) -> Result<(), ExecutionError> {
205 self.account
206 .try_sub_assign(fees)
207 .map_err(|_| ExecutionError::FeesExceedFunding {
208 fees,
209 balance: self.balance().unwrap_or(Amount::MAX),
210 })?;
211 Ok(())
212 }
213
214 pub(crate) fn remaining_fuel(&self, vm_runtime: VmRuntime) -> u64 {
216 let balance = self.balance().unwrap_or(Amount::MAX);
217 let fuel = self.tracker.as_ref().fuel(vm_runtime);
218 let maximum_fuel_per_block = self.policy.maximum_fuel_per_block(vm_runtime);
219 self.policy
220 .remaining_fuel(balance, vm_runtime)
221 .min(maximum_fuel_per_block.saturating_sub(fuel))
222 }
223
224 pub fn track_grant(&mut self, grant: Amount) -> Result<(), ExecutionError> {
226 self.tracker.as_mut().grants.try_add_assign(grant)?;
227 self.update_balance(grant)
228 }
229
230 pub fn track_operation(&mut self, operation: &Operation) -> Result<(), ExecutionError> {
232 self.tracker.as_mut().operations = self
233 .tracker
234 .as_mut()
235 .operations
236 .checked_add(1)
237 .ok_or(ArithmeticError::Overflow)?;
238 self.update_balance(self.policy.operation)?;
239 match operation {
240 Operation::System(_) => Ok(()),
241 Operation::User { bytes, .. } => {
242 let size = bytes.len();
243 self.tracker.as_mut().operation_bytes = self
244 .tracker
245 .as_mut()
246 .operation_bytes
247 .checked_add(size as u64)
248 .ok_or(ArithmeticError::Overflow)?;
249 self.update_balance(self.policy.operation_bytes_price(size as u64)?)?;
250 Ok(())
251 }
252 }
253 }
254
255 pub fn track_message(&mut self, message: &Message) -> Result<(), ExecutionError> {
257 self.tracker.as_mut().messages = self
258 .tracker
259 .as_mut()
260 .messages
261 .checked_add(1)
262 .ok_or(ArithmeticError::Overflow)?;
263 self.update_balance(self.policy.message)?;
264 match message {
265 Message::System(_) => Ok(()),
266 Message::User { bytes, .. } => {
267 let size = bytes.len();
268 self.tracker.as_mut().message_bytes = self
269 .tracker
270 .as_mut()
271 .message_bytes
272 .checked_add(size as u64)
273 .ok_or(ArithmeticError::Overflow)?;
274 self.update_balance(self.policy.message_bytes_price(size as u64)?)?;
275 Ok(())
276 }
277 }
278 }
279
280 pub fn track_http_request(&mut self) -> Result<(), ExecutionError> {
282 self.tracker.as_mut().http_requests = self
283 .tracker
284 .as_ref()
285 .http_requests
286 .checked_add(1)
287 .ok_or(ArithmeticError::Overflow)?;
288 self.update_balance(self.policy.http_request)
289 }
290
291 pub(crate) fn track_fuel(
293 &mut self,
294 fuel: u64,
295 vm_runtime: VmRuntime,
296 ) -> Result<(), ExecutionError> {
297 match vm_runtime {
298 VmRuntime::Wasm => {
299 self.tracker.as_mut().wasm_fuel = self
300 .tracker
301 .as_ref()
302 .wasm_fuel
303 .checked_add(fuel)
304 .ok_or(ArithmeticError::Overflow)?;
305 ensure!(
306 self.tracker.as_ref().wasm_fuel <= self.policy.maximum_wasm_fuel_per_block,
307 ExecutionError::MaximumFuelExceeded(vm_runtime)
308 );
309 }
310 VmRuntime::Evm => {
311 self.tracker.as_mut().evm_fuel = self
312 .tracker
313 .as_ref()
314 .evm_fuel
315 .checked_add(fuel)
316 .ok_or(ArithmeticError::Overflow)?;
317 ensure!(
318 self.tracker.as_ref().evm_fuel <= self.policy.maximum_evm_fuel_per_block,
319 ExecutionError::MaximumFuelExceeded(vm_runtime)
320 );
321 }
322 }
323 self.update_balance(self.policy.fuel_price(fuel, vm_runtime)?)
324 }
325
326 pub(crate) fn track_runtime_chain_id(&mut self) -> Result<(), ExecutionError> {
328 self.track_size_runtime_operations(RUNTIME_CHAIN_ID_SIZE)
329 }
330
331 pub(crate) fn track_runtime_block_height(&mut self) -> Result<(), ExecutionError> {
333 self.track_size_runtime_operations(RUNTIME_BLOCK_HEIGHT_SIZE)
334 }
335
336 pub(crate) fn track_runtime_application_id(&mut self) -> Result<(), ExecutionError> {
338 self.track_size_runtime_operations(RUNTIME_APPLICATION_ID_SIZE)
339 }
340
341 pub(crate) fn track_runtime_application_parameters(
343 &mut self,
344 parameters: &[u8],
345 ) -> Result<(), ExecutionError> {
346 let parameters_len = parameters.len() as u32;
347 self.track_size_runtime_operations(parameters_len)
348 }
349
350 pub(crate) fn track_runtime_timestamp(&mut self) -> Result<(), ExecutionError> {
352 self.track_size_runtime_operations(RUNTIME_TIMESTAMP_SIZE)
353 }
354
355 pub(crate) fn track_runtime_balance(&mut self) -> Result<(), ExecutionError> {
357 self.track_size_runtime_operations(RUNTIME_AMOUNT_SIZE)
358 }
359
360 pub(crate) fn track_runtime_owner_balances(
362 &mut self,
363 owner_balances: &[(AccountOwner, Amount)],
364 ) -> Result<(), ExecutionError> {
365 let mut size = 0;
366 for (account_owner, _) in owner_balances {
367 size += account_owner.size() + RUNTIME_AMOUNT_SIZE;
368 }
369 self.track_size_runtime_operations(size)
370 }
371
372 pub(crate) fn track_runtime_owners(
374 &mut self,
375 owners: &[AccountOwner],
376 ) -> Result<(), ExecutionError> {
377 let mut size = 0;
378 for owner in owners {
379 size += owner.size();
380 }
381 self.track_size_runtime_operations(size)
382 }
383
384 pub(crate) fn track_runtime_chain_ownership(
386 &mut self,
387 chain_ownership: &ChainOwnership,
388 ) -> Result<(), ExecutionError> {
389 let mut size = 0;
390 for account_owner in &chain_ownership.super_owners {
391 size += account_owner.size();
392 }
393 for account_owner in chain_ownership.owners.keys() {
394 size += account_owner.size() + RUNTIME_OWNER_WEIGHT_SIZE;
395 }
396 size += RUNTIME_CONSTANT_CHAIN_OWNERSHIP_SIZE;
397 self.track_size_runtime_operations(size)
398 }
399
400 fn track_size_runtime_operations(&mut self, size: u32) -> Result<(), ExecutionError> {
402 self.tracker.as_mut().bytes_runtime = self
403 .tracker
404 .as_mut()
405 .bytes_runtime
406 .checked_add(size)
407 .ok_or(ArithmeticError::Overflow)?;
408 self.update_balance(self.policy.bytes_runtime_price(size)?)
409 }
410
411 pub(crate) fn track_read_operation(&mut self) -> Result<(), ExecutionError> {
413 self.tracker.as_mut().read_operations = self
414 .tracker
415 .as_mut()
416 .read_operations
417 .checked_add(1)
418 .ok_or(ArithmeticError::Overflow)?;
419 self.update_balance(self.policy.read_operations_price(1)?)
420 }
421
422 pub(crate) fn track_write_operations(&mut self, count: u32) -> Result<(), ExecutionError> {
424 self.tracker.as_mut().write_operations = self
425 .tracker
426 .as_mut()
427 .write_operations
428 .checked_add(count)
429 .ok_or(ArithmeticError::Overflow)?;
430 self.update_balance(self.policy.write_operations_price(count)?)
431 }
432
433 pub(crate) fn track_bytes_read(&mut self, count: u64) -> Result<(), ExecutionError> {
435 self.tracker.as_mut().bytes_read = self
436 .tracker
437 .as_mut()
438 .bytes_read
439 .checked_add(count)
440 .ok_or(ArithmeticError::Overflow)?;
441 if self.tracker.as_mut().bytes_read >= self.policy.maximum_bytes_read_per_block {
442 return Err(ExecutionError::ExcessiveRead);
443 }
444 self.update_balance(self.policy.bytes_read_price(count)?)?;
445 Ok(())
446 }
447
448 pub(crate) fn track_bytes_written(&mut self, count: u64) -> Result<(), ExecutionError> {
450 self.tracker.as_mut().bytes_written = self
451 .tracker
452 .as_mut()
453 .bytes_written
454 .checked_add(count)
455 .ok_or(ArithmeticError::Overflow)?;
456 if self.tracker.as_mut().bytes_written >= self.policy.maximum_bytes_written_per_block {
457 return Err(ExecutionError::ExcessiveWrite);
458 }
459 self.update_balance(self.policy.bytes_written_price(count)?)?;
460 Ok(())
461 }
462
463 pub(crate) fn track_blob_read(&mut self, count: u64) -> Result<(), ExecutionError> {
465 {
466 let tracker = self.tracker.as_mut();
467 tracker.blob_bytes_read = tracker
468 .blob_bytes_read
469 .checked_add(count)
470 .ok_or(ArithmeticError::Overflow)?;
471 tracker.blobs_read = tracker
472 .blobs_read
473 .checked_add(1)
474 .ok_or(ArithmeticError::Overflow)?;
475 }
476 self.update_balance(self.policy.blob_read_price(count)?)?;
477 Ok(())
478 }
479
480 pub fn track_blob_published(&mut self, blob: &Blob) -> Result<(), ExecutionError> {
482 self.policy.check_blob_size(blob.content())?;
483 let size = blob.content().bytes().len() as u64;
484 if blob.is_committee_blob() {
485 return Ok(());
486 }
487 {
488 let tracker = self.tracker.as_mut();
489 tracker.blob_bytes_published = tracker
490 .blob_bytes_published
491 .checked_add(size)
492 .ok_or(ArithmeticError::Overflow)?;
493 tracker.blobs_published = tracker
494 .blobs_published
495 .checked_add(1)
496 .ok_or(ArithmeticError::Overflow)?;
497 }
498 self.update_balance(self.policy.blob_published_price(size)?)?;
499 Ok(())
500 }
501
502 #[allow(dead_code)]
505 pub(crate) fn track_stored_bytes(&mut self, delta: i32) -> Result<(), ExecutionError> {
506 self.tracker.as_mut().bytes_stored = self
507 .tracker
508 .as_mut()
509 .bytes_stored
510 .checked_add(delta)
511 .ok_or(ArithmeticError::Overflow)?;
512 Ok(())
513 }
514
515 pub(crate) fn remaining_service_oracle_execution_time(
517 &self,
518 ) -> Result<Duration, ExecutionError> {
519 let tracker = self.tracker.as_ref();
520 let spent_execution_time = tracker.service_oracle_execution;
521 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
522
523 limit
524 .checked_sub(spent_execution_time)
525 .ok_or(ExecutionError::MaximumServiceOracleExecutionTimeExceeded)
526 }
527
528 pub(crate) fn track_service_oracle_call(&mut self) -> Result<(), ExecutionError> {
530 self.tracker.as_mut().service_oracle_queries = self
531 .tracker
532 .as_mut()
533 .service_oracle_queries
534 .checked_add(1)
535 .ok_or(ArithmeticError::Overflow)?;
536 self.update_balance(self.policy.service_as_oracle_query)
537 }
538
539 pub(crate) fn track_service_oracle_execution(
541 &mut self,
542 execution_time: Duration,
543 ) -> Result<(), ExecutionError> {
544 let tracker = self.tracker.as_mut();
545 let spent_execution_time = &mut tracker.service_oracle_execution;
546 let limit = Duration::from_millis(self.policy.maximum_service_oracle_execution_ms);
547
548 *spent_execution_time = spent_execution_time.saturating_add(execution_time);
549
550 ensure!(
551 *spent_execution_time < limit,
552 ExecutionError::MaximumServiceOracleExecutionTimeExceeded
553 );
554
555 Ok(())
556 }
557
558 pub(crate) fn track_service_oracle_response(
560 &mut self,
561 response_bytes: usize,
562 ) -> Result<(), ExecutionError> {
563 ensure!(
564 response_bytes as u64 <= self.policy.maximum_oracle_response_bytes,
565 ExecutionError::ServiceOracleResponseTooLarge
566 );
567
568 Ok(())
569 }
570}
571
572impl<Account, Tracker> ResourceController<Account, Tracker>
573where
574 Tracker: AsMut<ResourceTracker>,
575{
576 pub fn track_block_size_of(&mut self, data: &impl Serialize) -> Result<(), ExecutionError> {
578 self.track_block_size(bcs::serialized_size(data)?)
579 }
580
581 pub fn track_block_size(&mut self, size: usize) -> Result<(), ExecutionError> {
583 let tracker = self.tracker.as_mut();
584 tracker.block_size = u64::try_from(size)
585 .ok()
586 .and_then(|size| tracker.block_size.checked_add(size))
587 .ok_or(ExecutionError::BlockTooLarge)?;
588 ensure!(
589 tracker.block_size <= self.policy.maximum_block_size,
590 ExecutionError::BlockTooLarge
591 );
592 Ok(())
593 }
594}
595
596impl ResourceController<Option<AccountOwner>, ResourceTracker> {
597 pub async fn with_state<'a, C>(
600 &mut self,
601 view: &'a mut SystemExecutionStateView<C>,
602 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
603 where
604 C: Context + Clone + Send + Sync + 'static,
605 {
606 self.with_state_and_grant(view, None).await
607 }
608
609 pub async fn with_state_and_grant<'a, C>(
613 &mut self,
614 view: &'a mut SystemExecutionStateView<C>,
615 grant: Option<&'a mut Amount>,
616 ) -> Result<ResourceController<Sources<'a>, &mut ResourceTracker>, ViewError>
617 where
618 C: Context + Clone + Send + Sync + 'static,
619 {
620 let mut sources = Vec::new();
621 if let Some(grant) = grant {
624 sources.push(grant);
625 } else {
626 sources.push(view.balance.get_mut());
627 }
628 if let Some(owner) = &self.account {
631 if let Some(balance) = view.balances.get_mut(owner).await? {
632 sources.push(balance);
633 }
634 }
635
636 Ok(ResourceController {
637 policy: self.policy.clone(),
638 tracker: &mut self.tracker,
639 account: Sources { sources },
640 })
641 }
642}
643
644impl BalanceHolder for Amount {
646 fn balance(&self) -> Result<Amount, ArithmeticError> {
647 Ok(*self)
648 }
649
650 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
651 self.try_add_assign(other)
652 }
653
654 fn try_sub_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
655 self.try_sub_assign(other)
656 }
657}
658
659impl AsMut<ResourceTracker> for ResourceTracker {
662 fn as_mut(&mut self) -> &mut Self {
663 self
664 }
665}
666
667impl AsRef<ResourceTracker> for ResourceTracker {
668 fn as_ref(&self) -> &Self {
669 self
670 }
671}
672
673pub struct Sources<'a> {
675 sources: Vec<&'a mut Amount>,
676}
677
678impl BalanceHolder for Sources<'_> {
679 fn balance(&self) -> Result<Amount, ArithmeticError> {
680 let mut amount = Amount::ZERO;
681 for source in self.sources.iter() {
682 amount.try_add_assign(**source)?;
683 }
684 Ok(amount)
685 }
686
687 fn try_add_assign(&mut self, other: Amount) -> Result<(), ArithmeticError> {
688 let source = self.sources.last_mut().expect("at least one source");
691 source.try_add_assign(other)
692 }
693
694 fn try_sub_assign(&mut self, mut other: Amount) -> Result<(), ArithmeticError> {
695 for source in self.sources.iter_mut() {
696 if source.try_sub_assign(other).is_ok() {
697 return Ok(());
698 }
699 other.try_sub_assign(**source).expect("*source < other");
700 **source = Amount::ZERO;
701 }
702 if other > Amount::ZERO {
703 Err(ArithmeticError::Underflow)
704 } else {
705 Ok(())
706 }
707 }
708}