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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
use std::rc::Rc;

use contract_transcode::{ContractMessageTranscoder, Value};
use parity_scale_codec::{Decode, Encode};

use crate::{
    errors::MessageResult,
    runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime, Runtime},
    session::{error::SessionError, BalanceOf},
    EventRecordOf, Sandbox,
};

type ContractInstantiateResult<R> =
    pallet_contracts::ContractInstantiateResult<AccountIdFor<R>, BalanceOf<R>, EventRecordOf<R>>;
type ContractExecResult<R> = pallet_contracts::ContractExecResult<BalanceOf<R>, EventRecordOf<R>>;

/// Data structure storing the results of contract interaction during a session.
///
/// # Naming convention
///
/// By `result` we mean the full result (enriched with some context information) of the contract
/// interaction, like `ContractExecResult`. By `return` we mean the return value of the contract
/// execution, like a value returned from a message or the address of a newly instantiated contract.
pub struct Record<T: pallet_contracts::Config> {
    /// The results of contract instantiation.
    deploy_results: Vec<ContractInstantiateResult<T>>,
    /// The return values of contract instantiation (i.e. the addresses of the newly instantiated
    /// contracts).
    deploy_returns: Vec<AccountIdFor<T>>,

    /// The results of contract calls.
    call_results: Vec<ContractExecResult<T>>,
    /// The return values of contract calls (in the SCALE-encoded form).
    call_returns: Vec<Vec<u8>>,

    /// The events emitted by the contracts.
    event_batches: Vec<EventBatch<T>>,

    /// Because `drink` normally doesn't have a continuous block production, everything implicitly
    /// happens within a single block (unless user explicitly trigger a new block). This means that
    /// all runtime events (from consecutive transactions) are stacked up in a common buffer.
    /// `Record` is capable of recording only the events that happened during a single transaction
    /// by remembering the number of events that were already in the buffer before the transaction
    /// started. However, this is must be explicitly enabled by calling `start_recording_events`
    /// before the transaction and `stop_recording_events` after the transaction.
    block_events_so_far: Option<usize>,
}

impl<T: pallet_contracts::Config> Default for Record<T> {
    fn default() -> Self {
        Self {
            deploy_results: Vec::new(),
            deploy_returns: Vec::new(),
            call_results: Vec::new(),
            call_returns: Vec::new(),
            event_batches: Vec::new(),
            block_events_so_far: None,
        }
    }
}

// API for `Session` to record results and events related to contract interaction.
impl<T: pallet_contracts::Config> Record<T> {
    pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult<T>) {
        self.deploy_results.push(result);
    }

    pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor<T>) {
        self.deploy_returns.push(return_value);
    }

    pub(super) fn push_call_result(&mut self, result: ContractExecResult<T>) {
        self.call_results.push(result);
    }

    pub(super) fn push_call_return(&mut self, return_value: Vec<u8>) {
        self.call_returns.push(return_value);
    }

    pub(super) fn start_recording_events(
        &mut self,
        sandbox: &mut Sandbox<impl Runtime<Config = T>>,
    ) {
        assert!(
            self.block_events_so_far.is_none(),
            "Already recording events"
        );
        self.block_events_so_far = Some(sandbox.events().len());
    }

    pub(super) fn stop_recording_events(
        &mut self,
        sandbox: &mut Sandbox<impl Runtime<Config = T>>,
    ) {
        let start = self
            .block_events_so_far
            .take()
            .expect("Not recording events");
        let end = sandbox.events().len();
        let events = sandbox.events()[start..end].to_vec();
        self.event_batches.push(EventBatch { events });
    }
}

// API for the end user.
impl<T: pallet_contracts::Config> Record<T> {
    /// Returns all the results of contract instantiations that happened during the session.
    pub fn deploy_results(&self) -> &[ContractInstantiateResult<T>] {
        &self.deploy_results
    }

    /// Returns the last result of contract instantiation that happened during the session. Panics
    /// if there were no contract instantiations.
    pub fn last_deploy_result(&self) -> &ContractInstantiateResult<T> {
        self.deploy_results.last().expect("No deploy results")
    }

    /// Returns all the return values of contract instantiations that happened during the session.
    pub fn deploy_returns(&self) -> &[AccountIdFor<T>] {
        &self.deploy_returns
    }

    /// Returns the last return value of contract instantiation that happened during the session.
    /// Panics if there were no contract instantiations.
    pub fn last_deploy_return(&self) -> &AccountIdFor<T> {
        self.deploy_returns.last().expect("No deploy returns")
    }

    /// Returns all the results of contract calls that happened during the session.
    pub fn call_results(&self) -> &[ContractExecResult<T>] {
        &self.call_results
    }

    /// Returns the last result of contract call that happened during the session. Panics if there
    /// were no contract calls.
    pub fn last_call_result(&self) -> &ContractExecResult<T> {
        self.call_results.last().expect("No call results")
    }

    /// Returns all the (encoded) return values of contract calls that happened during the session.
    pub fn call_returns(&self) -> &[Vec<u8>] {
        &self.call_returns
    }

    /// Returns the last (encoded) return value of contract call that happened during the session.
    /// Panics if there were no contract calls.
    pub fn last_call_return(&self) -> &[u8] {
        self.call_returns.last().expect("No call returns")
    }

    /// Returns the last (decoded) return value of contract call that happened during the session.
    /// Panics if there were no contract calls.
    pub fn last_call_return_decoded<V: Decode>(&self) -> Result<MessageResult<V>, SessionError> {
        let mut raw = self.last_call_return();
        MessageResult::decode(&mut raw).map_err(|err| {
            SessionError::Decoding(format!(
                "Failed to decode the result of calling a contract: {err:?}"
            ))
        })
    }

    /// Returns all the event batches that were recorded for contract interactions during the
    /// session.
    pub fn event_batches(&self) -> &[EventBatch<T>] {
        &self.event_batches
    }

    /// Returns the last event batch that was recorded for contract interactions during the session.
    /// Panics if there were no event batches.
    pub fn last_event_batch(&self) -> &EventBatch<T> {
        self.event_batches.last().expect("No event batches")
    }
}

/// A batch of runtime events that were emitted during a single contract interaction.
pub struct EventBatch<T: frame_system::Config> {
    events: Vec<EventRecordOf<T>>,
}

impl<T: frame_system::Config> EventBatch<T> {
    /// Returns all the events that were emitted during the contract interaction.
    pub fn all_events(&self) -> &[EventRecordOf<T>] {
        &self.events
    }
}

impl EventBatch<MinimalRuntime> {
    /// Returns all the contract events that were emitted during the contract interaction.
    ///
    /// **WARNING**: This method will return all the events that were emitted by ANY contract. If your
    /// call triggered multiple contracts, you will have to filter the events yourself.
    ///
    /// We have to match against static enum variant, and thus (at least for now) we support only
    /// `MinimalRuntime`.
    pub fn contract_events(&self) -> Vec<&[u8]> {
        self.events
            .iter()
            .filter_map(|event| match &event.event {
                RuntimeEvent::Contracts(
                    pallet_contracts::Event::<MinimalRuntime>::ContractEmitted { data, .. },
                ) => Some(data.as_slice()),
                _ => None,
            })
            .collect()
    }

    /// The same as `contract_events`, but decodes the events using the given transcoder.
    ///
    /// **WARNING**: This method will try to decode all the events that were emitted by ANY
    /// contract. This means that some contract events might either fail to decode or be decoded
    /// incorrectly (to some rubbish). In the former case, they will be skipped, but with the latter
    /// case, you will have to filter the events yourself.
    ///
    /// **WARNING 2**: This method will ignore anonymous events.
    pub fn contract_events_decoded(
        &self,
        transcoder: &Rc<ContractMessageTranscoder>,
    ) -> Vec<Value> {
        let signature_topics = transcoder
            .metadata()
            .spec()
            .events()
            .iter()
            .filter_map(|event| event.signature_topic())
            .map(|sig| sig.as_bytes().try_into().unwrap())
            .collect::<Vec<[u8; 32]>>();

        self.contract_events()
            .into_iter()
            .filter_map(|data| {
                for signature_topic in &signature_topics {
                    if let Ok(decoded) = transcoder
                        // We have to `encode` the data because `decode_contract_event` is targeted
                        // at decoding the data from the runtime, and not directly from the contract
                        // events.
                        .decode_contract_event(&signature_topic.into(), &mut &*data.encode())
                    {
                        return Some(decoded);
                    }
                }
                None
            })
            .collect()
    }
}