ink_engine/
ext.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
15//! Provides the same interface as Substrate's FRAME `revive` module.
16//!
17//! See [the documentation for the `revive` module](https://docs.rs/crate/pallet-revive)
18//! for more information.
19
20use crate::{
21    database::Database,
22    exec_context::ExecContext,
23    test_api::{
24        DebugInfo,
25        EmittedEvent,
26    },
27    types::BlockTimestamp,
28};
29use hex_literal::hex;
30use ink_primitives::{
31    Address,
32    U256,
33};
34pub use pallet_revive_uapi::ReturnErrorCode as Error;
35use std::panic::panic_any;
36
37/// The off-chain engine.
38pub struct Engine {
39    /// The environment database.
40    pub database: Database,
41    /// The current execution context.
42    pub exec_context: ExecContext,
43    /// Recorder for relevant interactions with the engine.
44    /// This is specifically about debug info. This info is
45    /// not available in the `contracts` pallet.
46    pub(crate) debug_info: DebugInfo,
47    /// The chain specification.
48    pub chain_spec: ChainSpec,
49}
50
51/// The chain specification.
52pub struct ChainSpec {
53    /// The current gas price.
54    pub gas_price: U256,
55    /// The minimum value an account of the chain must have
56    /// (i.e. the chain's existential deposit).
57    pub minimum_balance: U256,
58    /// The targeted block time.
59    pub block_time: BlockTimestamp,
60}
61
62/// The default values for the chain specification are:
63///
64///   * `gas_price`: 100
65///   * `minimum_balance`: 42
66///   * `block_time`: 6
67///
68/// There is no particular reason behind choosing them this way.
69impl Default for ChainSpec {
70    fn default() -> Self {
71        Self {
72            gas_price: 100.into(),
73            minimum_balance: 42.into(),
74            block_time: 6,
75        }
76    }
77}
78
79impl Engine {
80    // Creates a new `Engine` instance.
81    pub fn new() -> Self {
82        Self {
83            database: Database::new(),
84            exec_context: ExecContext::new(),
85            debug_info: DebugInfo::new(),
86            chain_spec: ChainSpec::default(),
87        }
88    }
89}
90
91impl Default for Engine {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl Engine {
98    /// Transfers value from the contract to the destination account.
99    #[allow(clippy::arithmetic_side_effects)] // todo
100    pub fn transfer(&mut self, dest: Address, mut value: &[u8]) -> Result<(), Error> {
101        // Note that a transfer of `0` is allowed here
102        let increment = <u128 as scale::Decode>::decode(&mut value)
103            .map_err(|_| Error::TransferFailed)?;
104
105        // Note that the destination account does not have to exist
106        let dest_old_balance = self.get_balance(dest).unwrap_or_default();
107
108        let contract = self.get_callee();
109        let contract_old_balance = self
110            .get_balance(contract)
111            .map_err(|_| Error::TransferFailed)?;
112
113        self.database
114            .set_balance(&contract, contract_old_balance - increment);
115        self.database
116            .set_balance(&dest, dest_old_balance + increment);
117        Ok(())
118    }
119
120    /// Deposits an event identified by the supplied topics and data.
121    pub fn deposit_event(&mut self, topics: &[[u8; 32]], data: &[u8]) {
122        self.debug_info.record_event(EmittedEvent {
123            topics: topics.to_vec(),
124            data: data.to_vec(),
125        });
126    }
127
128    /// Writes the encoded value into the storage at the given key.
129    /// Returns the size of the previously stored value at the key if any.
130    pub fn set_storage(&mut self, key: &[u8], encoded_value: &[u8]) -> Option<u32> {
131        let callee = self.get_callee();
132
133        self.debug_info.inc_writes(callee);
134        self.debug_info
135            .record_cell_for_account(callee, key.to_vec());
136
137        self.database
138            .insert_into_contract_storage(&callee, key, encoded_value.to_vec())
139            .map(|v| <u32>::try_from(v.len()).expect("usize to u32 conversion failed"))
140    }
141
142    /// Returns the contract storage bytes at the key if any.
143    pub fn get_storage(&mut self, key: &[u8]) -> Result<&[u8], Error> {
144        let callee = self.get_callee();
145
146        self.debug_info.inc_reads(callee);
147        match self.database.get_from_contract_storage(&callee, key) {
148            Some(val) => Ok(val),
149            None => Err(Error::KeyNotFound),
150        }
151    }
152
153    /// Removes the storage entries at the given key,
154    /// returning previously stored value at the key if any.
155    pub fn take_storage(&mut self, key: &[u8]) -> Result<Vec<u8>, Error> {
156        let callee = self.get_callee();
157
158        self.debug_info.inc_writes(callee);
159        match self.database.remove_contract_storage(&callee, key) {
160            Some(val) => Ok(val),
161            None => Err(Error::KeyNotFound),
162        }
163    }
164
165    /// Returns the size of the value stored in the contract storage at the key if any.
166    pub fn contains_storage(&mut self, key: &[u8]) -> Option<u32> {
167        let callee = self.get_callee();
168
169        self.debug_info.inc_reads(callee);
170        self.database
171            .get_from_contract_storage(&callee, key)
172            .map(|val| val.len() as u32)
173    }
174
175    /// Removes the storage entries at the given key.
176    /// Returns the size of the previously stored value at the key if any.
177    pub fn clear_storage(&mut self, key: &[u8]) -> Option<u32> {
178        let callee = self.get_callee();
179        self.debug_info.inc_writes(callee);
180        let _ = self
181            .debug_info
182            .remove_cell_for_account(callee, key.to_vec());
183        self.database
184            .remove_contract_storage(&callee, key)
185            .map(|val| val.len() as u32)
186    }
187
188    /// Remove the calling account and transfer remaining balance.
189    ///
190    /// todo is the following comment still correct?
191    /// This function never returns. Either the termination was successful and the
192    /// execution of the destroyed contract is halted. Or it failed during the
193    /// termination which is considered fatal.
194    pub fn terminate(&mut self, beneficiary: Address) -> ! {
195        // Send the remaining balance to the beneficiary
196        let contract = self.get_callee();
197        let all = self
198            .get_balance(contract)
199            .unwrap_or_else(|err| panic!("could not get balance: {err:?}"));
200        let value = &scale::Encode::encode(&all)[..];
201        self.transfer(beneficiary, value)
202            .unwrap_or_else(|err| panic!("transfer did not work: {err:?}"));
203
204        // Encode the result of the termination and panic with it.
205        // This enables testing for the proper result and makes sure this
206        // method returns `Never`.
207        let res = (all, beneficiary);
208        panic_any(scale::Encode::encode(&res));
209    }
210
211    /// Returns the address of the caller.
212    pub fn caller(&self, output: &mut &mut [u8]) {
213        let caller = self.exec_context.caller;
214        let caller = scale::Encode::encode(&caller);
215        set_output(output, &caller[..])
216    }
217
218    /// Returns the balance of the executed contract.
219    pub fn balance(&self, output: &mut &mut [u8]) {
220        let contract = self
221            .exec_context
222            .callee
223            .as_ref()
224            .expect("no callee has been set");
225
226        let balance_in_storage = self
227            .database
228            .get_balance(contract)
229            .expect("currently executing contract must exist");
230        let balance = scale::Encode::encode(&balance_in_storage);
231        set_output(output, &balance[..])
232    }
233
234    /// Returns the transferred value for the called contract.
235    pub fn value_transferred(&self, output: &mut &mut [u8]) {
236        let value_transferred: Vec<u8> =
237            scale::Encode::encode(&self.exec_context.value_transferred);
238        set_output(output, &value_transferred[..])
239    }
240
241    /// Returns the address of the executed contract.
242    pub fn address(&self, output: &mut &mut [u8]) {
243        let callee = self
244            .exec_context
245            .callee
246            .as_ref()
247            .expect("no callee has been set")
248            .as_bytes();
249        set_output(output, callee)
250    }
251
252    /// Conduct the BLAKE-2 256-bit hash and place the result into `output`.
253    pub fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
254        super::hashing::blake2b_256(input, output);
255    }
256
257    /// Conduct the BLAKE-2 128-bit hash and place the result into `output`.
258    pub fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
259        super::hashing::blake2b_128(input, output);
260    }
261
262    /// Conduct the SHA-2 256-bit hash and place the result into `output`.
263    pub fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
264        super::hashing::sha2_256(input, output);
265    }
266
267    /// Conduct the KECCAK 256-bit hash and place the result into `output`.
268    pub fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
269        super::hashing::keccak_256(input, output);
270    }
271
272    /// Returns the current block number.
273    pub fn block_number(&self, output: &mut &mut [u8]) {
274        let block_number: Vec<u8> =
275            scale::Encode::encode(&self.exec_context.block_number);
276        set_output(output, &block_number[..])
277    }
278
279    /// Returns the timestamp of the current block.
280    pub fn block_timestamp(&self, output: &mut &mut [u8]) {
281        let block_timestamp: Vec<u8> =
282            scale::Encode::encode(&self.exec_context.block_timestamp);
283        set_output(output, &block_timestamp[..])
284    }
285
286    /// Returns the minimum balance that is required for creating an account
287    /// (i.e. the chain's existential deposit).
288    pub fn minimum_balance(&self, output: &mut &mut [u8]) {
289        let minimum_balance: Vec<u8> =
290            scale::Encode::encode(&self.chain_spec.minimum_balance);
291        set_output(output, &minimum_balance[..])
292    }
293
294    #[allow(clippy::too_many_arguments)]
295    pub fn instantiate(
296        &mut self,
297        _code_hash: &[u8],
298        _gas_limit: u64,
299        _endowment: &[u8],
300        _input: &[u8],
301        _out_address: &mut &mut [u8],
302        _out_return_value: &mut &mut [u8],
303        _salt: &[u8],
304    ) -> Result<(), Error> {
305        unimplemented!("off-chain environment does not yet support `instantiate`");
306    }
307
308    pub fn call(
309        &mut self,
310        callee: &[u8],
311        _gas_limit: u64,
312        _value: &[u8],
313        input: &[u8],
314        output: &mut &mut [u8],
315    ) -> Result<(), Error> {
316        const ECRECOVER: [u8; 20] = hex!("0000000000000000000000000000000000000001");
317        if callee == ECRECOVER {
318            let mut signature = [0u8; 65];
319            signature.copy_from_slice(&input[..65]);
320            let mut message_hash = [0u8; 32];
321            message_hash.copy_from_slice(&input[65..65 + 32]);
322
323            let out: &mut [u8; 33] = output
324                .as_mut()
325                .try_into()
326                .expect("Slice must be exactly 33 bytes long");
327            let _ = self.ecdsa_recover(&signature, &message_hash, out);
328        }
329        unimplemented!(
330            "off-chain environment does not yet support `call` for non-precompiles"
331        );
332    }
333
334    /// Emulates gas price calculation.
335    pub fn weight_to_fee(&self, gas: u64, output: &mut &mut [u8]) {
336        let fee = self.chain_spec.gas_price.saturating_mul(gas.into());
337        let fee: Vec<u8> = scale::Encode::encode(&fee);
338        set_output(output, &fee[..])
339    }
340}
341
342impl Engine {
343    /// Recovers the compressed ECDSA public key for given `signature` and `message_hash`,
344    /// and stores the result in `output`.
345    #[allow(clippy::arithmetic_side_effects)] // todo
346    pub fn ecdsa_recover(
347        &mut self,
348        signature: &[u8; 65],
349        message_hash: &[u8; 32],
350        output: &mut [u8; 33],
351    ) -> Result<(), Error> {
352        use secp256k1::{
353            Message,
354            SECP256K1,
355            ecdsa::{
356                RecoverableSignature,
357                RecoveryId,
358            },
359        };
360
361        // In most implementations, the v is just 0 or 1 internally, but 27 was added
362        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
363        // as well.
364        let recovery_byte = if signature[64] > 26 {
365            signature[64] - 27
366        } else {
367            signature[64]
368        };
369
370        let recovery_id = RecoveryId::try_from(recovery_byte as i32)
371            .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {error}"));
372
373        let message = Message::from_digest_slice(message_hash).unwrap_or_else(|error| {
374            panic!("Unable to create the message from hash: {error}")
375        });
376        let signature =
377            RecoverableSignature::from_compact(&signature[0..64], recovery_id)
378                .unwrap_or_else(|error| panic!("Unable to parse the signature: {error}"));
379
380        let pub_key = SECP256K1.recover_ecdsa(&message, &signature);
381        match pub_key {
382            Ok(pub_key) => {
383                *output = pub_key.serialize();
384                Ok(())
385            }
386            Err(_) => Err(Error::EcdsaRecoveryFailed),
387        }
388    }
389}
390
391/// Copies the `slice` into `output`.
392///
393/// Panics if the slice is too large and does not fit.
394fn set_output(output: &mut &mut [u8], slice: &[u8]) {
395    assert!(
396        slice.len() <= output.len(),
397        "the output buffer is too small! the decoded storage is of size {} bytes, \
398        but the output buffer has only room for {}.",
399        slice.len(),
400        output.len(),
401    );
402    output[..slice.len()].copy_from_slice(slice);
403}