linera_execution/
transaction_tracker.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::vec;
5
6use linera_base::{
7    data_types::{Amount, ArithmeticError, OracleResponse},
8    ensure,
9    identifiers::ApplicationId,
10};
11
12use crate::{
13    ExecutionError, ExecutionOutcome, RawExecutionOutcome, SystemExecutionError, SystemMessage,
14};
15
16/// Tracks oracle responses and execution outcomes of an ongoing transaction execution, as well
17/// as replayed oracle responses.
18#[derive(Debug, Default)]
19pub struct TransactionTracker {
20    replaying_oracle_responses: Option<vec::IntoIter<OracleResponse>>,
21    oracle_responses: Vec<OracleResponse>,
22    outcomes: Vec<ExecutionOutcome>,
23    next_message_index: u32,
24}
25
26impl TransactionTracker {
27    pub fn new(next_message_index: u32, oracle_responses: Option<Vec<OracleResponse>>) -> Self {
28        TransactionTracker {
29            replaying_oracle_responses: oracle_responses.map(Vec::into_iter),
30            next_message_index,
31            oracle_responses: Vec::new(),
32            outcomes: Vec::new(),
33        }
34    }
35
36    pub fn next_message_index(&self) -> u32 {
37        self.next_message_index
38    }
39
40    pub fn add_system_outcome(
41        &mut self,
42        outcome: RawExecutionOutcome<SystemMessage, Amount>,
43    ) -> Result<(), ArithmeticError> {
44        self.add_outcome(ExecutionOutcome::System(outcome))
45    }
46
47    pub fn add_user_outcome(
48        &mut self,
49        application_id: ApplicationId,
50        outcome: RawExecutionOutcome<Vec<u8>, Amount>,
51    ) -> Result<(), ArithmeticError> {
52        self.add_outcome(ExecutionOutcome::User(application_id, outcome))
53    }
54
55    pub fn add_outcomes(
56        &mut self,
57        outcomes: impl IntoIterator<Item = ExecutionOutcome>,
58    ) -> Result<(), ArithmeticError> {
59        for outcome in outcomes {
60            self.add_outcome(outcome)?;
61        }
62        Ok(())
63    }
64
65    fn add_outcome(&mut self, outcome: ExecutionOutcome) -> Result<(), ArithmeticError> {
66        let message_count =
67            u32::try_from(outcome.message_count()).map_err(|_| ArithmeticError::Overflow)?;
68        self.next_message_index = self
69            .next_message_index
70            .checked_add(message_count)
71            .ok_or(ArithmeticError::Overflow)?;
72        self.outcomes.push(outcome);
73        Ok(())
74    }
75
76    pub fn add_oracle_response(&mut self, oracle_response: OracleResponse) {
77        self.oracle_responses.push(oracle_response);
78    }
79
80    /// Adds the oracle response to the record.
81    /// If replaying, it also checks that it matches the next replayed one and returns `true`.
82    pub fn replay_oracle_response(
83        &mut self,
84        oracle_response: OracleResponse,
85    ) -> Result<bool, SystemExecutionError> {
86        let replaying = if let Some(recorded_response) = self.next_replayed_oracle_response()? {
87            ensure!(
88                recorded_response == oracle_response,
89                SystemExecutionError::OracleResponseMismatch
90            );
91            true
92        } else {
93            false
94        };
95        self.add_oracle_response(oracle_response);
96        Ok(replaying)
97    }
98
99    pub fn next_replayed_oracle_response(
100        &mut self,
101    ) -> Result<Option<OracleResponse>, SystemExecutionError> {
102        let Some(responses) = &mut self.replaying_oracle_responses else {
103            return Ok(None); // Not in replay mode.
104        };
105        let response = responses
106            .next()
107            .ok_or_else(|| SystemExecutionError::MissingOracleResponse)?;
108        Ok(Some(response))
109    }
110
111    pub fn destructure(
112        self,
113    ) -> Result<(Vec<ExecutionOutcome>, Vec<OracleResponse>, u32), ExecutionError> {
114        let TransactionTracker {
115            replaying_oracle_responses,
116            oracle_responses,
117            outcomes,
118            next_message_index,
119        } = self;
120        if let Some(mut responses) = replaying_oracle_responses {
121            ensure!(
122                responses.next().is_none(),
123                ExecutionError::UnexpectedOracleResponse
124            );
125        }
126        Ok((outcomes, oracle_responses, next_message_index))
127    }
128
129    pub(crate) fn outcomes_mut(&mut self) -> &mut Vec<ExecutionOutcome> {
130        &mut self.outcomes
131    }
132}