1use crate::{
21 chain_extension::ChainExtensionHandler,
22 database::Database,
23 exec_context::ExecContext,
24 test_api::{
25 DebugInfo,
26 EmittedEvent,
27 },
28 types::{
29 BlockTimestamp,
30 H160,
31 },
32};
33use hex_literal::hex;
34use ink_primitives::U256;
35pub use pallet_revive_uapi::ReturnErrorCode as Error;
36use scale::Encode;
37use std::panic::panic_any;
38
39pub struct Engine {
41 pub database: Database,
43 pub exec_context: ExecContext,
45 pub(crate) debug_info: DebugInfo,
49 pub chain_spec: ChainSpec,
51 pub chain_extension_handler: ChainExtensionHandler,
53}
54
55pub struct ChainSpec {
57 pub gas_price: U256,
59 pub minimum_balance: U256,
62 pub block_time: BlockTimestamp,
64}
65
66impl Default for ChainSpec {
74 fn default() -> Self {
75 Self {
76 gas_price: 100.into(),
77 minimum_balance: 42.into(),
78 block_time: 6,
79 }
80 }
81}
82
83impl Engine {
84 pub fn new() -> Self {
86 Self {
87 database: Database::new(),
88 exec_context: ExecContext::new(),
89 debug_info: DebugInfo::new(),
90 chain_spec: ChainSpec::default(),
91 chain_extension_handler: ChainExtensionHandler::new(),
92 }
93 }
94}
95
96impl Default for Engine {
97 fn default() -> Self {
98 Self::new()
99 }
100}
101
102impl Engine {
103 #[allow(clippy::arithmetic_side_effects)] pub fn transfer(&mut self, dest: H160, mut value: &[u8]) -> Result<(), Error> {
106 let increment = <u128 as scale::Decode>::decode(&mut value)
108 .map_err(|_| Error::TransferFailed)?;
109
110 let dest_old_balance = self.get_balance(dest).unwrap_or_default();
112
113 let contract = self.get_callee();
114 let contract_old_balance = self
115 .get_balance(contract)
116 .map_err(|_| Error::TransferFailed)?;
117
118 self.database
119 .set_balance(&contract, contract_old_balance - increment);
120 self.database
121 .set_balance(&dest, dest_old_balance + increment);
122 Ok(())
123 }
124
125 #[allow(clippy::arithmetic_side_effects)] pub fn deposit_event(&mut self, topics: &[u8], data: &[u8]) {
128 let topics_count: scale::Compact<u32> = scale::Decode::decode(&mut &topics[0..1])
130 .unwrap_or_else(|err| panic!("decoding number of topics failed: {err}"));
131 let topics_count = topics_count.0 as usize;
132
133 let topics_vec = if topics_count > 0 {
134 let topics = &topics[1..];
136 let bytes_per_topic = topics.len() / topics_count;
137 let topics_vec: Vec<Vec<u8>> = topics
138 .chunks(bytes_per_topic)
139 .map(|chunk| chunk.to_vec())
140 .collect();
141 assert_eq!(topics_count, topics_vec.len());
142 topics_vec
143 } else {
144 Vec::new()
145 };
146
147 self.debug_info.record_event(EmittedEvent {
148 topics: topics_vec,
149 data: data.to_vec(),
150 });
151 }
152
153 pub fn set_storage(&mut self, key: &[u8], encoded_value: &[u8]) -> Option<u32> {
156 let callee = self.get_callee();
157
158 self.debug_info.inc_writes(callee);
159 self.debug_info
160 .record_cell_for_account(callee, key.to_vec());
161
162 self.database
163 .insert_into_contract_storage(&callee, key, encoded_value.to_vec())
164 .map(|v| <u32>::try_from(v.len()).expect("usize to u32 conversion failed"))
165 }
166
167 pub fn get_storage(&mut self, key: &[u8]) -> Result<&[u8], Error> {
169 let callee = self.get_callee();
170
171 self.debug_info.inc_reads(callee);
172 match self.database.get_from_contract_storage(&callee, key) {
173 Some(val) => Ok(val),
174 None => Err(Error::KeyNotFound),
175 }
176 }
177
178 pub fn take_storage(&mut self, key: &[u8]) -> Result<Vec<u8>, Error> {
181 let callee = self.get_callee();
182
183 self.debug_info.inc_writes(callee);
184 match self.database.remove_contract_storage(&callee, key) {
185 Some(val) => Ok(val),
186 None => Err(Error::KeyNotFound),
187 }
188 }
189
190 pub fn contains_storage(&mut self, key: &[u8]) -> Option<u32> {
192 let callee = self.get_callee();
193
194 self.debug_info.inc_reads(callee);
195 self.database
196 .get_from_contract_storage(&callee, key)
197 .map(|val| val.len() as u32)
198 }
199
200 pub fn clear_storage(&mut self, key: &[u8]) -> Option<u32> {
203 let callee = self.get_callee();
204 self.debug_info.inc_writes(callee);
205 let _ = self
206 .debug_info
207 .remove_cell_for_account(callee, key.to_vec());
208 self.database
209 .remove_contract_storage(&callee, key)
210 .map(|val| val.len() as u32)
211 }
212
213 pub fn terminate(&mut self, beneficiary: H160) -> ! {
220 let contract = self.get_callee();
222 let all = self
223 .get_balance(contract)
224 .unwrap_or_else(|err| panic!("could not get balance: {err:?}"));
225 let value = &scale::Encode::encode(&all)[..];
226 self.transfer(beneficiary, value)
227 .unwrap_or_else(|err| panic!("transfer did not work: {err:?}"));
228
229 let res = (all, beneficiary);
233 panic_any(scale::Encode::encode(&res));
234 }
235
236 pub fn caller(&self, output: &mut &mut [u8]) {
238 let caller = self.exec_context.caller;
239 let caller = scale::Encode::encode(&caller);
240 set_output(output, &caller[..])
241 }
242
243 pub fn balance(&self, output: &mut &mut [u8]) {
245 let contract = self
246 .exec_context
247 .callee
248 .as_ref()
249 .expect("no callee has been set");
250
251 let balance_in_storage = self
252 .database
253 .get_balance(contract)
254 .expect("currently executing contract must exist");
255 let balance = scale::Encode::encode(&balance_in_storage);
256 set_output(output, &balance[..])
257 }
258
259 pub fn value_transferred(&self, output: &mut &mut [u8]) {
261 let value_transferred: Vec<u8> =
262 scale::Encode::encode(&self.exec_context.value_transferred);
263 set_output(output, &value_transferred[..])
264 }
265
266 pub fn address(&self, output: &mut &mut [u8]) {
268 let callee = self
269 .exec_context
270 .callee
271 .as_ref()
272 .expect("no callee has been set")
273 .as_bytes();
274 set_output(output, callee)
275 }
276
277 pub fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
279 super::hashing::blake2b_256(input, output);
280 }
281
282 pub fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
284 super::hashing::blake2b_128(input, output);
285 }
286
287 pub fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
289 super::hashing::sha2_256(input, output);
290 }
291
292 pub fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
294 super::hashing::keccak_256(input, output);
295 }
296
297 pub fn block_number(&self, output: &mut &mut [u8]) {
299 let block_number: Vec<u8> =
300 scale::Encode::encode(&self.exec_context.block_number);
301 set_output(output, &block_number[..])
302 }
303
304 pub fn block_timestamp(&self, output: &mut &mut [u8]) {
306 let block_timestamp: Vec<u8> =
307 scale::Encode::encode(&self.exec_context.block_timestamp);
308 set_output(output, &block_timestamp[..])
309 }
310
311 pub fn minimum_balance(&self, output: &mut &mut [u8]) {
314 let minimum_balance: Vec<u8> =
315 scale::Encode::encode(&self.chain_spec.minimum_balance);
316 set_output(output, &minimum_balance[..])
317 }
318
319 #[allow(clippy::too_many_arguments)]
320 pub fn instantiate(
321 &mut self,
322 _code_hash: &[u8],
323 _gas_limit: u64,
324 _endowment: &[u8],
325 _input: &[u8],
326 _out_address: &mut &mut [u8],
327 _out_return_value: &mut &mut [u8],
328 _salt: &[u8],
329 ) -> Result<(), Error> {
330 unimplemented!("off-chain environment does not yet support `instantiate`");
331 }
332
333 pub fn call(
334 &mut self,
335 callee: &[u8],
336 _gas_limit: u64,
337 _value: &[u8],
338 input: &[u8],
339 output: &mut &mut [u8],
340 ) -> Result<(), Error> {
341 const ECRECOVER: [u8; 20] = hex!("0000000000000000000000000000000000000001");
342 if callee == ECRECOVER {
343 let mut signature = [0u8; 65];
344 signature.copy_from_slice(&input[..65]);
345 let mut message_hash = [0u8; 32];
346 message_hash.copy_from_slice(&input[65..65 + 32]);
347
348 let out: &mut [u8; 33] = output
349 .as_mut()
350 .try_into()
351 .expect("Slice must be exactly 33 bytes long");
352 let _ = self.ecdsa_recover(&signature, &message_hash, out);
353 }
354 unimplemented!(
355 "off-chain environment does not yet support `call` for non-precompiles"
356 );
357 }
358
359 pub fn weight_to_fee(&self, gas: u64, output: &mut &mut [u8]) {
361 let fee = self.chain_spec.gas_price.saturating_mul(gas.into());
362 let fee: Vec<u8> = scale::Encode::encode(&fee);
363 set_output(output, &fee[..])
364 }
365
366 pub fn call_chain_extension(
368 &mut self,
369 id: u32,
370 input: &[u8],
371 output: &mut &mut [u8],
372 ) {
373 let encoded_input = input.encode();
374 let (status_code, out) = self
375 .chain_extension_handler
376 .eval(id, &encoded_input)
377 .unwrap_or_else(|error| {
378 panic!(
379 "Encountered unexpected missing chain extension method: {error:?}"
380 );
381 });
382 let res = (status_code, out);
383 let decoded: Vec<u8> = scale::Encode::encode(&res);
384 set_output(output, &decoded[..])
385 }
386}
387
388impl Engine {
389 #[allow(clippy::arithmetic_side_effects)] pub fn ecdsa_recover(
393 &mut self,
394 signature: &[u8; 65],
395 message_hash: &[u8; 32],
396 output: &mut [u8; 33],
397 ) -> Result<(), Error> {
398 use secp256k1::{
399 ecdsa::{
400 RecoverableSignature,
401 RecoveryId,
402 },
403 Message,
404 SECP256K1,
405 };
406
407 let recovery_byte = if signature[64] > 26 {
411 signature[64] - 27
412 } else {
413 signature[64]
414 };
415
416 let recovery_id = RecoveryId::try_from(recovery_byte as i32)
417 .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {error}"));
418
419 let message = Message::from_digest_slice(message_hash).unwrap_or_else(|error| {
420 panic!("Unable to create the message from hash: {error}")
421 });
422 let signature =
423 RecoverableSignature::from_compact(&signature[0..64], recovery_id)
424 .unwrap_or_else(|error| panic!("Unable to parse the signature: {error}"));
425
426 let pub_key = SECP256K1.recover_ecdsa(&message, &signature);
427 match pub_key {
428 Ok(pub_key) => {
429 *output = pub_key.serialize();
430 Ok(())
431 }
432 Err(_) => Err(Error::EcdsaRecoveryFailed),
433 }
434 }
435}
436
437fn set_output(output: &mut &mut [u8], slice: &[u8]) {
441 assert!(
442 slice.len() <= output.len(),
443 "the output buffer is too small! the decoded storage is of size {} bytes, \
444 but the output buffer has only room for {}.",
445 slice.len(),
446 output.len(),
447 );
448 output[..slice.len()].copy_from_slice(slice);
449}