1use 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
37pub struct Engine {
39 pub database: Database,
41 pub exec_context: ExecContext,
43 pub(crate) debug_info: DebugInfo,
47 pub chain_spec: ChainSpec,
49}
50
51pub struct ChainSpec {
53 pub gas_price: U256,
55 pub minimum_balance: U256,
58 pub block_time: BlockTimestamp,
60}
61
62impl 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 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 #[allow(clippy::arithmetic_side_effects)] pub fn transfer(&mut self, dest: Address, mut value: &[u8]) -> Result<(), Error> {
101 let increment = <u128 as scale::Decode>::decode(&mut value)
103 .map_err(|_| Error::TransferFailed)?;
104
105 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 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 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 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 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 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 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 pub fn terminate(&mut self, beneficiary: Address) -> ! {
195 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 let res = (all, beneficiary);
208 panic_any(scale::Encode::encode(&res));
209 }
210
211 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 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 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 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 pub fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
254 super::hashing::blake2b_256(input, output);
255 }
256
257 pub fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
259 super::hashing::blake2b_128(input, output);
260 }
261
262 pub fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
264 super::hashing::sha2_256(input, output);
265 }
266
267 pub fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
269 super::hashing::keccak_256(input, output);
270 }
271
272 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 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 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 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 #[allow(clippy::arithmetic_side_effects)] 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 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
391fn 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}