ink_engine/
test_api.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{
16    ext::Engine,
17    types::{
18        Balance,
19        BlockNumber,
20        BlockTimestamp,
21    },
22    AccountError,
23    Error,
24};
25use ink_primitives::{
26    AccountId,
27    Address,
28    U256,
29};
30use std::collections::HashMap;
31
32/// Record for an emitted event.
33#[derive(Debug, Clone)]
34pub struct EmittedEvent {
35    /// Recorded topics of the emitted event.
36    pub topics: Vec<Vec<u8>>,
37    /// Recorded encoding of the emitted event.
38    pub data: Vec<u8>,
39}
40
41/// Recorder for relevant interactions with this crate.
42pub struct DebugInfo {
43    /// Emitted events recorder.
44    emitted_events: Vec<EmittedEvent>,
45    /// The total number of reads to the storage.
46    count_reads: HashMap<Address, usize>,
47    /// The total number of writes to the storage.
48    count_writes: HashMap<Address, usize>,
49    /// The number of storage cells used by each contract.
50    cells_per_contract: HashMap<Address, HashMap<Vec<u8>, bool>>,
51}
52
53impl Default for DebugInfo {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl DebugInfo {
60    // Creates a new `RecInstance instance.
61    pub fn new() -> Self {
62        Self {
63            emitted_events: Vec::new(),
64            count_reads: HashMap::new(),
65            count_writes: HashMap::new(),
66            cells_per_contract: HashMap::new(),
67        }
68    }
69
70    /// Resets the recorder.
71    pub fn reset(&mut self) {
72        self.count_reads.clear();
73        self.count_writes.clear();
74        self.emitted_events.clear();
75        self.cells_per_contract.clear();
76    }
77
78    /// Increases the number of storage writes for the supplied account by one.
79    #[allow(clippy::arithmetic_side_effects)] // todo
80    pub fn inc_writes(&mut self, addr: Address) {
81        self.count_writes
82            .entry(addr)
83            .and_modify(|v| *v += 1)
84            .or_insert(1);
85    }
86
87    /// Increases the number of storage reads for the supplied account by one.
88    #[allow(clippy::arithmetic_side_effects)] // todo
89    pub fn inc_reads(&mut self, addr: Address) {
90        self.count_reads
91            .entry(addr)
92            .and_modify(|v| *v += 1)
93            .or_insert(1);
94    }
95
96    /// Records that a cell exists for an account under `key`.
97    ///
98    /// Calling this function multiple times won't change the fact that only
99    /// one cell is recorded.
100    pub fn record_cell_for_account(&mut self, addr: Address, key: Vec<u8>) {
101        self.cells_per_contract
102            .entry(addr)
103            .and_modify(|hm| {
104                let _ = hm.insert(key.clone(), true);
105            })
106            .or_insert({
107                let mut hm = HashMap::new();
108                hm.insert(key, true);
109                hm
110            });
111    }
112
113    /// Removes the cell under `key` for the supplied account.
114    ///
115    /// Returns the removed cell, if there was one.
116    pub fn remove_cell_for_account(
117        &mut self,
118        addr: Address,
119        key: Vec<u8>,
120    ) -> Option<bool> {
121        self.cells_per_contract
122            .get_mut(&addr)
123            .map(|hm| hm.remove(&key))
124            .unwrap_or(None)
125    }
126
127    /// Records an event.
128    pub fn record_event(&mut self, event: EmittedEvent) {
129        self.emitted_events.push(event);
130    }
131}
132
133impl Engine {
134    /// Resets the environment.
135    pub fn initialize_or_reset(&mut self) {
136        self.exec_context.reset();
137        self.database.clear();
138        self.debug_info.reset();
139    }
140
141    /// Returns the total number of reads and writes of the contract's storage.
142    pub fn get_contract_storage_rw(&self, addr: Address) -> (usize, usize) {
143        let reads = self.debug_info.count_reads.get(&addr).unwrap_or(&0);
144        let writes = self.debug_info.count_writes.get(&addr).unwrap_or(&0);
145        (*reads, *writes)
146    }
147
148    /// Returns the total number of reads executed.
149    pub fn count_reads(&self) -> usize {
150        self.debug_info.count_reads.values().sum()
151    }
152
153    /// Returns the total number of writes executed.
154    pub fn count_writes(&self) -> usize {
155        self.debug_info.count_writes.values().sum()
156    }
157
158    /// Sets a caller for the next call.
159    pub fn set_caller(&mut self, caller: Address) {
160        self.exec_context.caller = caller;
161    }
162
163    /// Sets a known contract by adding it to a vector of known contracts accounts
164    pub fn set_contract(&mut self, caller: Address) {
165        self.exec_context.contracts.push(caller);
166    }
167
168    /// Sets the callee for the next call.
169    pub fn set_callee(&mut self, callee: Address) {
170        self.exec_context.callee = Some(callee);
171    }
172
173    /// Returns the amount of storage cells used by the contract `addr`.
174    ///
175    /// Returns `None` if the contract `addr` is non-existent.
176    pub fn count_used_storage_cells(&self, addr: &Address) -> Result<usize, Error> {
177        let cells = self
178            .debug_info
179            .cells_per_contract
180            .get(addr)
181            .ok_or(Error::Account(AccountError::NoContractForId(*addr)))?;
182        Ok(cells.len())
183    }
184
185    /// Advances the chain by a single block.
186    pub fn advance_block(&mut self) {
187        self.exec_context.block_number = self
188            .exec_context
189            .block_number
190            .checked_add(1)
191            .expect("failed to add");
192        self.exec_context.block_timestamp = self
193            .exec_context
194            .block_timestamp
195            .checked_add(self.chain_spec.block_time)
196            .expect("failed to add");
197    }
198
199    /// Returns the callee, i.e. the currently executing contract.
200    pub fn get_callee(&self) -> Address {
201        self.exec_context.callee()
202    }
203
204    /// Returns boolean value indicating whether the account is a contract
205    pub fn is_contract(&self, addr: &Address) -> bool {
206        self.exec_context.contracts.contains(addr)
207    }
208
209    /// Returns the recorded emitted events in order.
210    pub fn get_emitted_events(&self) -> impl Iterator<Item = EmittedEvent> {
211        self.debug_info.emitted_events.clone().into_iter()
212    }
213
214    /// Returns the current balance of `addr`.
215    pub fn get_acc_balance(&self, addr: AccountId) -> Result<Balance, Error> {
216        self.database
217            .get_acc_balance(&addr)
218            .ok_or(Error::Account(AccountError::NoAccountForId(addr)))
219    }
220
221    /// Sets the balance of `addr` to `new_balance`.
222    pub fn set_acc_balance(&mut self, addr: AccountId, new_balance: Balance) {
223        self.database.set_acc_balance(&addr, new_balance);
224    }
225
226    /// Returns the current balance of `addr`.
227    pub fn get_balance(&self, addr: Address) -> Result<U256, Error> {
228        self.database
229            .get_balance(&addr)
230            .ok_or(Error::Account(AccountError::NoContractForId(addr)))
231    }
232
233    /// Sets the balance of `addr` to `new_balance`.
234    pub fn set_balance(&mut self, addr: Address, new_balance: U256) {
235        self.database.set_balance(&addr, new_balance);
236    }
237
238    /// Sets the value transferred from the caller to the callee as part of the call.
239    pub fn set_value_transferred(&mut self, value: U256) {
240        self.exec_context.value_transferred = value;
241    }
242
243    /// Set the block timestamp for the execution context.
244    pub fn set_block_timestamp(&mut self, new_block_timestamp: BlockTimestamp) {
245        self.exec_context.block_timestamp = new_block_timestamp;
246    }
247
248    /// Set the block number for the execution context.
249    pub fn set_block_number(&mut self, new_block_number: BlockNumber) {
250        self.exec_context.block_number = new_block_number;
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn setting_getting_callee() {
260        let mut engine = Engine::new();
261        let addr = Address::from([1; 20]);
262        engine.set_callee(addr);
263        assert_eq!(engine.get_callee(), addr);
264    }
265
266    #[test]
267    fn count_cells_per_account_must_stay_the_same() {
268        // given
269        let mut engine = Engine::new();
270        let addr = Address::from([1; 20]);
271        engine.set_callee(addr);
272        let key: &[u8; 32] = &[0x42; 32];
273        engine.set_storage(key, &[0x05_u8; 5]);
274        assert_eq!(engine.count_used_storage_cells(&addr), Ok(1));
275
276        // when
277        // we set the storage a second time
278        engine.set_storage(key, &[0x05_u8; 6]);
279
280        // then
281        // the amount of storage cells used must have stayed the same
282        assert_eq!(engine.count_used_storage_cells(&addr), Ok(1));
283    }
284
285    #[test]
286    fn count_cells_per_account_must_be_reset() {
287        // given
288        let mut engine = Engine::new();
289        let addr = Address::from([1; 20]);
290        engine.set_callee(addr);
291        let key: &[u8; 32] = &[0x42; 32];
292        engine.set_storage(key, &[0x05_u8; 5]);
293        assert_eq!(engine.count_used_storage_cells(&addr), Ok(1));
294
295        // when
296        engine.clear_storage(key);
297
298        // then
299        assert_eq!(engine.count_used_storage_cells(&addr), Ok(0));
300    }
301
302    #[test]
303    fn count_total_writes() {
304        // given
305        let mut engine = Engine::new();
306        let key: &[u8; 32] = &[0x42; 32];
307
308        // when
309        engine.set_callee(Address::from([1; 20]));
310        engine.set_storage(key, &[0x05_u8; 5]);
311        engine.set_storage(key, &[0x05_u8; 6]);
312        engine.get_storage(key).unwrap();
313
314        engine.set_callee(Address::from([2; 20]));
315        engine.set_storage(key, &[0x07_u8; 7]);
316        engine.get_storage(key).unwrap();
317
318        // then
319        assert_eq!(engine.count_writes(), 3);
320        assert_eq!(engine.count_reads(), 2);
321    }
322}