pchain_runtime/contract/
functions.rs

1/*
2    Copyright © 2023, ParallelChain Lab
3    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
4*/
5
6//! Implementation for host functions used for contract methods according to [crate::contract::cbi].
7
8use ed25519_dalek::Verifier;
9use pchain_types::{
10    blockchain::{Command, Log},
11    runtime::CallInput,
12    serialization::{Deserializable, Serializable},
13};
14use pchain_world_state::{
15    keys::AppKey, network::constants::NETWORK_ADDRESS, storage::WorldStateStorage,
16};
17use ripemd::Ripemd160;
18use sha2::{Digest as sha256_digest, Sha256};
19use tiny_keccak::{Hasher as _, Keccak};
20
21use crate::{
22    contract::{ContractBinaryInterface, FuncError},
23    cost::CostChange,
24    execution::{self},
25    gas::{self},
26    types::DeferredCommand,
27    wasmer::wasmer_env::Env,
28};
29
30/// `ContractBinaryFunctions` implements trait [ContractBinaryInterface] that defines all host functions that are used for instantiating contract for calling contract method.
31/// ### CBI version: 0
32pub(crate) struct ContractBinaryFunctions {}
33impl<S> ContractBinaryInterface<Env<S>> for ContractBinaryFunctions
34where
35    S: WorldStateStorage + Sync + Send + Clone,
36{
37    fn set(
38        env: &Env<S>,
39        key_ptr: u32,
40        key_len: u32,
41        val_ptr: u32,
42        val_len: u32,
43    ) -> Result<(), FuncError> {
44        let app_key = env.read_bytes(key_ptr, key_len)?;
45        let app_key = AppKey::new(app_key);
46
47        let new_value = env.read_bytes(val_ptr, val_len)?;
48
49        let target_address = env.call_tx.target;
50
51        let cost_change =
52            env.context
53                .lock()
54                .unwrap()
55                .set_app_data(target_address, app_key, new_value);
56        env.consume_non_wasm_gas(cost_change);
57        Ok(())
58    }
59
60    fn get(env: &Env<S>, key_ptr: u32, key_len: u32, val_ptr_ptr: u32) -> Result<i64, FuncError> {
61        let app_key = env.read_bytes(key_ptr, key_len)?;
62        let app_key = AppKey::new(app_key);
63
64        let tx_ctx_lock = env.context.lock().unwrap();
65        let (value, cost_change) = tx_ctx_lock.app_data(env.call_tx.target, app_key);
66        drop(tx_ctx_lock);
67
68        env.consume_non_wasm_gas(cost_change);
69
70        let ret_val = match value {
71            Some(value) => env.write_bytes(value, val_ptr_ptr)? as i64,
72            None => -1,
73        };
74
75        Ok(ret_val)
76    }
77
78    fn get_network_storage(
79        env: &Env<S>,
80        key_ptr: u32,
81        key_len: u32,
82        val_ptr_ptr: u32,
83    ) -> Result<i64, FuncError> {
84        let app_key = env.read_bytes(key_ptr, key_len)?;
85        let app_key = AppKey::new(app_key);
86
87        let tx_ctx_lock = env.context.lock().unwrap();
88        let (value, cost_change) = tx_ctx_lock.app_data(NETWORK_ADDRESS, app_key);
89        drop(tx_ctx_lock);
90
91        env.consume_non_wasm_gas(cost_change);
92
93        let ret_val = match value {
94            Some(value) => env.write_bytes(value, val_ptr_ptr)? as i64,
95            None => -1,
96        };
97
98        Ok(ret_val)
99    }
100
101    fn balance(env: &Env<S>) -> Result<u64, FuncError> {
102        let (balance, cost_change) = env.context.lock().unwrap().balance(env.call_tx.target);
103        env.consume_non_wasm_gas(cost_change);
104        Ok(balance)
105    }
106
107    fn block_height(env: &Env<S>) -> Result<u64, FuncError> {
108        Ok(env.params_from_blockchain.this_block_number)
109    }
110    fn block_timestamp(env: &Env<S>) -> Result<u32, FuncError> {
111        Ok(env.params_from_blockchain.timestamp)
112    }
113    fn prev_block_hash(env: &Env<S>, hash_ptr_ptr: u32) -> Result<(), FuncError> {
114        env.write_bytes(
115            env.params_from_blockchain.prev_block_hash.to_vec(),
116            hash_ptr_ptr,
117        )?;
118        Ok(())
119    }
120
121    fn calling_account(env: &Env<S>, address_ptr_ptr: u32) -> Result<(), FuncError> {
122        env.write_bytes(env.call_tx.signer.to_vec(), address_ptr_ptr)?;
123        Ok(())
124    }
125    fn current_account(env: &Env<S>, address_ptr_ptr: u32) -> Result<(), FuncError> {
126        env.write_bytes(env.call_tx.target.to_vec(), address_ptr_ptr)?;
127        Ok(())
128    }
129
130    fn method(env: &Env<S>, method_ptr_ptr: u32) -> Result<u32, FuncError> {
131        env.write_bytes(env.call_tx.method.as_bytes().to_vec(), method_ptr_ptr)
132    }
133
134    fn arguments(env: &Env<S>, arguments_ptr_ptr: u32) -> Result<u32, FuncError> {
135        match &env.call_tx.arguments {
136            Some(arguments) => {
137                let arguments = <Vec<Vec<u8>> as Serializable>::serialize(arguments);
138                env.write_bytes(arguments, arguments_ptr_ptr)
139            }
140            None => Ok(0),
141        }
142    }
143
144    fn amount(env: &Env<S>) -> Result<u64, FuncError> {
145        Ok(env.call_tx.amount.map_or(0, std::convert::identity))
146    }
147
148    fn is_internal_call(env: &Env<S>) -> Result<i32, FuncError> {
149        Ok(i32::from(env.call_counter != 0))
150    }
151
152    fn transaction_hash(env: &Env<S>, hash_ptr_ptr: u32) -> Result<(), FuncError> {
153        env.write_bytes(env.call_tx.hash.to_vec(), hash_ptr_ptr)?;
154        Ok(())
155    }
156
157    fn log(env: &Env<S>, log_ptr: u32, log_len: u32) -> Result<(), FuncError> {
158        let serialized_log = env.read_bytes(log_ptr, log_len)?;
159        let log = Log::deserialize(&serialized_log).map_err(|e| FuncError::Runtime(e.into()))?;
160
161        let cost_change =
162            CostChange::deduct(gas::blockchain_log_cost(log.topic.len(), log.value.len()));
163        let mut tx_ctx_lock = env.context.lock().unwrap();
164        tx_ctx_lock.receipt_write_gas += cost_change;
165        drop(tx_ctx_lock);
166
167        // check exhaustion before writing receipt data to ensure
168        // the data is not written to receipt after gas exhaustion
169        env.consume_non_wasm_gas(cost_change);
170        if env.get_wasmer_remaining_points() == 0 {
171            return Err(FuncError::GasExhaustionError);
172        }
173
174        env.context.lock().unwrap().logs.push(log);
175
176        Ok(())
177    }
178
179    fn return_value(env: &Env<S>, value_ptr: u32, value_len: u32) -> Result<(), FuncError> {
180        let value = env.read_bytes(value_ptr, value_len)?;
181
182        let cost_change = CostChange::deduct(gas::blockchain_return_values_cost(value.len()));
183        let mut tx_ctx_lock = env.context.lock().unwrap();
184        tx_ctx_lock.receipt_write_gas += cost_change;
185        drop(tx_ctx_lock);
186
187        // check exhaustion before writing receipt data to ensure
188        // the data is not written to receipt after gas exhaustion
189        env.consume_non_wasm_gas(cost_change);
190        if env.get_wasmer_remaining_points() == 0 {
191            return Err(FuncError::GasExhaustionError);
192        }
193
194        env.context.lock().unwrap().return_value =
195            if value.is_empty() { None } else { Some(value) };
196
197        Ok(())
198    }
199
200    fn call(
201        env: &Env<S>,
202        call_input_ptr: u32,
203        call_input_len: u32,
204        return_ptr_ptr: u32,
205    ) -> Result<u32, FuncError> {
206        let call_command_bytes = env.read_bytes(call_input_ptr, call_input_len)?;
207        let call_command =
208            Command::deserialize(&call_command_bytes).map_err(|e| FuncError::Runtime(e.into()))?;
209
210        let (target, method, arguments, amount) = match call_command {
211            Command::Call(CallInput {
212                target,
213                method,
214                arguments,
215                amount,
216            }) => (target, method, arguments, amount),
217            _ => return Err(FuncError::Internal),
218        };
219
220        // error if transfer amount is specified in view call.
221        if env.is_view && amount.is_some() {
222            return Err(FuncError::Internal);
223        }
224
225        // obtain the signer address (this contract's address) from transaction execution context
226        let signer = env.call_tx.target;
227        // gas limit bounded by remaining gas
228        let gas_limit = env.get_wasmer_remaining_points();
229
230        // by default, fields would be inherited from parent transaction
231        let mut call_tx = env.call_tx.clone();
232        call_tx.signer = signer;
233        call_tx.gas_limit = gas_limit;
234
235        call_tx.target = target;
236        call_tx.method = method;
237        call_tx.arguments = arguments;
238        call_tx.amount = amount;
239
240        let result = execution::internal::call_from_contract(
241            call_tx,
242            env.params_from_blockchain.clone(),
243            env.context.clone(),
244            env.call_counter.saturating_add(1),
245            env.is_view,
246        );
247        env.consume_non_wasm_gas(result.non_wasmer_gas);
248        env.consume_wasm_gas(result.exec_gas); // subtract gas consumed from parent contract's environment
249
250        match result.error {
251            None => {
252                let mut tx_ctx_locked = env.context.lock().unwrap();
253                let res = tx_ctx_locked.return_value.clone();
254
255                // clear child result in parent's execution context. No cost because the return value is not written to block.
256                tx_ctx_locked.return_value = None;
257
258                if let Some(res) = res {
259                    return env.write_bytes(res, return_ptr_ptr);
260                }
261            }
262            Some(e) => {
263                if env.get_wasmer_remaining_points() == 0 {
264                    return Err(FuncError::GasExhaustionError);
265                }
266                return Err(e);
267            }
268        }
269
270        Ok(0)
271    }
272
273    fn transfer(env: &Env<S>, transfer_input_ptr: u32) -> Result<(), FuncError> {
274        let transfer_bytes =
275            env.read_bytes(transfer_input_ptr, std::mem::size_of::<[u8; 40]>() as u32)?;
276
277        let (recipient, amount_bytes) = transfer_bytes.split_at(32);
278        let recipient = recipient.try_into().unwrap();
279        let amount = u64::from_le_bytes(amount_bytes.try_into().unwrap());
280
281        let result = execution::internal::transfer_from_contract(
282            env.call_tx.target, // the signer address (this contract's address) from transaction execution context
283            amount,
284            recipient,
285            env.context.clone(),
286        );
287        env.consume_non_wasm_gas(result.non_wasmer_gas);
288
289        match result.error {
290            None => Ok(()),
291            Some(e) => Err(e),
292        }
293    }
294
295    fn defer_create_deposit(
296        env: &Env<S>,
297        create_deposit_input_ptr: u32,
298        create_deposit_input_len: u32,
299    ) -> Result<(), FuncError> {
300        let serialized_command =
301            env.read_bytes(create_deposit_input_ptr, create_deposit_input_len)?;
302        let command =
303            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
304
305        if !matches!(command, Command::CreateDeposit { .. }) {
306            return Err(FuncError::Internal);
307        }
308
309        env.context.lock().unwrap().commands.push(DeferredCommand {
310            command,
311            contract_address: env.call_tx.target,
312        });
313
314        Ok(())
315    }
316
317    fn defer_set_deposit_settings(
318        env: &Env<S>,
319        set_deposit_settings_input_ptr: u32,
320        set_deposit_settings_input_len: u32,
321    ) -> Result<(), FuncError> {
322        let serialized_command = env.read_bytes(
323            set_deposit_settings_input_ptr,
324            set_deposit_settings_input_len,
325        )?;
326        let command =
327            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
328
329        if !matches!(command, Command::SetDepositSettings { .. }) {
330            return Err(FuncError::Internal);
331        }
332
333        env.context.lock().unwrap().commands.push(DeferredCommand {
334            command,
335            contract_address: env.call_tx.target,
336        });
337
338        Ok(())
339    }
340
341    fn defer_topup_deposit(
342        env: &Env<S>,
343        top_up_deposit_input_ptr: u32,
344        top_up_deposit_input_len: u32,
345    ) -> Result<(), FuncError> {
346        let serialized_command =
347            env.read_bytes(top_up_deposit_input_ptr, top_up_deposit_input_len)?;
348        let command =
349            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
350
351        if !matches!(command, Command::TopUpDeposit { .. }) {
352            return Err(FuncError::Internal);
353        }
354
355        env.context.lock().unwrap().commands.push(DeferredCommand {
356            command,
357            contract_address: env.call_tx.target,
358        });
359
360        Ok(())
361    }
362
363    fn defer_withdraw_deposit(
364        env: &Env<S>,
365        withdraw_deposit_input_ptr: u32,
366        withdraw_deposit_input_len: u32,
367    ) -> Result<(), FuncError> {
368        let serialized_command =
369            env.read_bytes(withdraw_deposit_input_ptr, withdraw_deposit_input_len)?;
370        let command =
371            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
372
373        if !matches!(command, Command::WithdrawDeposit { .. }) {
374            return Err(FuncError::Internal);
375        }
376
377        env.context.lock().unwrap().commands.push(DeferredCommand {
378            command,
379            contract_address: env.call_tx.target,
380        });
381
382        Ok(())
383    }
384
385    fn defer_stake_deposit(
386        env: &Env<S>,
387        stake_deposit_input_ptr: u32,
388        stake_deposit_input_len: u32,
389    ) -> Result<(), FuncError> {
390        let serialized_command =
391            env.read_bytes(stake_deposit_input_ptr, stake_deposit_input_len)?;
392        let command =
393            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
394
395        if !matches!(command, Command::StakeDeposit { .. }) {
396            return Err(FuncError::Internal);
397        }
398
399        env.context.lock().unwrap().commands.push(DeferredCommand {
400            command,
401            contract_address: env.call_tx.target,
402        });
403
404        Ok(())
405    }
406
407    fn defer_unstake_deposit(
408        env: &Env<S>,
409        unstake_deposit_input_ptr: u32,
410        unstake_deposit_input_len: u32,
411    ) -> Result<(), FuncError> {
412        let serialized_command =
413            env.read_bytes(unstake_deposit_input_ptr, unstake_deposit_input_len)?;
414        let command =
415            Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
416
417        if !matches!(command, Command::UnstakeDeposit { .. }) {
418            return Err(FuncError::Internal);
419        }
420
421        env.context.lock().unwrap().commands.push(DeferredCommand {
422            command,
423            contract_address: env.call_tx.target,
424        });
425
426        Ok(())
427    }
428
429    fn sha256(
430        env: &Env<S>,
431        msg_ptr: u32,
432        msg_len: u32,
433        digest_ptr_ptr: u32,
434    ) -> Result<(), FuncError> {
435        let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
436
437        env.consume_wasm_gas(gas::CRYPTO_SHA256_PER_BYTE * input_bytes.len() as u64);
438
439        let mut hasher = Sha256::new();
440        sha2::Digest::update(&mut hasher, input_bytes);
441        let digest = hasher.finalize().to_vec();
442
443        env.write_bytes(digest, digest_ptr_ptr)?;
444        Ok(())
445    }
446
447    fn keccak256(
448        env: &Env<S>,
449        msg_ptr: u32,
450        msg_len: u32,
451        digest_ptr_ptr: u32,
452    ) -> Result<(), FuncError> {
453        let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
454        let mut output_bytes = [0u8; 32];
455
456        env.consume_wasm_gas(gas::CRYPTO_KECCAK256_PER_BYTE * input_bytes.len() as u64);
457
458        let mut keccak = Keccak::v256();
459        keccak.update(&input_bytes);
460        keccak.finalize(&mut output_bytes);
461        let digest = output_bytes.to_vec();
462
463        env.write_bytes(digest, digest_ptr_ptr)?;
464        Ok(())
465    }
466
467    fn ripemd(
468        env: &Env<S>,
469        msg_ptr: u32,
470        msg_len: u32,
471        digest_ptr_ptr: u32,
472    ) -> Result<(), FuncError> {
473        let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
474
475        env.consume_wasm_gas(gas::CRYPTO_RIPEMD160_PER_BYTE * input_bytes.len() as u64);
476
477        let mut hasher = Ripemd160::new();
478        hasher.update(&input_bytes);
479        let digest = hasher.finalize().to_vec();
480
481        env.write_bytes(digest, digest_ptr_ptr)?;
482        Ok(())
483    }
484
485    fn verify_ed25519_signature(
486        env: &Env<S>,
487        msg_ptr: u32,
488        msg_len: u32,
489        signature_ptr: u32,
490        address_ptr: u32,
491    ) -> Result<i32, FuncError> {
492        let message = env.read_bytes(msg_ptr, msg_len)?;
493
494        let signature = env.read_bytes(signature_ptr, 64)?;
495
496        let address = env.read_bytes(address_ptr, 32)?;
497
498        env.consume_wasm_gas(gas::crypto_verify_ed25519_signature_cost(message.len()));
499
500        let public_key =
501            ed25519_dalek::PublicKey::from_bytes(&address).map_err(|_| FuncError::Internal)?;
502
503        let dalek_signature =
504            ed25519_dalek::Signature::from_bytes(&signature).map_err(|_| FuncError::Internal)?;
505
506        let result = public_key.verify(&message, &dalek_signature).is_ok();
507
508        Ok(result as i32)
509    }
510}