pchain_runtime/contract/
cbi.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//! Definition of host functions that are imported by ParallelChain Smart Contracts.
7//!
8//! The definitions follows the specification in [ParallelChain protocol](https://github.com/parallelchain-io/parallelchain-protocol/blob/master/Contracts.md).
9
10use wasmer::{imports, Function, ImportObject, Store};
11
12use super::MethodCallError;
13
14/// Definition of host functions with [wasmer::WasmerEnv]. Implement this trait for creation of importable
15/// that can be used in instantiation of contract module.
16///
17/// Host function arguments with suffix `_ptr_ptr` are namely pointer-to-pointer variable that
18/// is considered as mutable reference to memory as an output value. Method `wasmer_memory::MemoryContext::set_return_values_to_memory`
19/// can be used to set output value into this variable.
20///
21/// Host functions arguments with suffix `_ptr` are namely pointer-to variable that
22/// is considers as immutable reference to memory as an input value. Method `wasmer_memory::MemoryContext::read_bytes`
23/// can be used to read the value from this variable.
24pub trait ContractBinaryInterface<T>
25where
26    T: wasmer::WasmerEnv + 'static,
27{
28    /// Sets a key to a value in the current Contract Account’s Storage.
29    fn set(
30        env: &T,
31        key_ptr: u32,
32        key_len: u32,
33        value_ptr: u32,
34        value_len: u32,
35    ) -> Result<(), FuncError>;
36
37    /// Gets the value corresponding to a key in the current Contract Account’s Storage.
38    /// It returns the length of the value.
39    fn get(env: &T, key_ptr: u32, key_len: u32, value_ptr_ptr: u32) -> Result<i64, FuncError>;
40
41    /// Gets the value corresponding to a key in the Network Account’s Storage.
42    /// It returns the length of the value.
43    fn get_network_storage(
44        env: &T,
45        key_ptr: u32,
46        key_len: u32,
47        value_ptr_ptr: u32,
48    ) -> Result<i64, FuncError>;
49
50    /// Get the balance of current account
51    fn balance(env: &T) -> Result<u64, FuncError>;
52
53    /// Gets the Height of the Block which the Transaction at the start of the current Call Chain is included in.
54    fn block_height(env: &T) -> Result<u64, FuncError>;
55
56    /// Gets the Timestamp of the Block which the Transaction at the start of the current Call Chain is included in.
57    fn block_timestamp(env: &T) -> Result<u32, FuncError>;
58
59    /// Get the Hash field of the previous Block.
60    /// - `hash_ptr_ptr` points to memory of 32 bytes address.
61    fn prev_block_hash(env: &T, hash_ptr_ptr: u32) -> Result<(), FuncError>;
62
63    /// Gets the Address of the Account that triggered the current Call. This could either be an External
64    /// Account (if the Call is directly triggered by a Call Transaction), or a Contract Account (if the Call is an Internal Call).
65    /// - `address_ptr_ptr` points to memory of 32 bytes address.
66    /// - returns the length of the value.
67    fn calling_account(env: &T, address_ptr_ptr: u32) -> Result<(), FuncError>;
68
69    /// Gets the Address of the current Account.
70    /// - `address_ptr_ptr` points to memory of 32 bytes address.
71    /// - returns the length of the value.
72    fn current_account(env: &T, address_ptr_ptr: u32) -> Result<(), FuncError>;
73
74    /// Gets the Method of the current Call.
75    /// - `method_ptr_ptr` points to memory of bytes.
76    /// - returns the length of the value.
77    fn method(env: &T, method_ptr_ptr: u32) -> Result<u32, FuncError>;
78
79    /// Gets the Arguments of the current Call.
80    /// - `arguments_ptr_ptr` points to memory of bytes.
81    /// - returns the length of the value.
82    fn arguments(env: &T, arguments_ptr_ptr: u32) -> Result<u32, FuncError>;
83
84    /// get transaction value of this transaction.
85    /// - returns the amount.
86    fn amount(env: &T) -> Result<u64, FuncError>;
87
88    /// Returns whether the current Call is an Internal Call.
89    fn is_internal_call(env: &T) -> Result<i32, FuncError>;
90
91    /// get transaction hash of this transaction.
92    /// -`hash_ptr_ptr` points to memory of 32 bytes data.
93    fn transaction_hash(env: &T, hash_ptr_ptr: u32) -> Result<(), FuncError>;
94
95    /// call methods of another contract.
96    /// - `call_ptr` points to memory of [pchain_types::blockchain::Command::Call]
97    /// - `return_ptr_ptr` points to memory of bytes.
98    /// - returns the length of Return Value.
99    fn call(
100        env: &T,
101        call_input_ptr: u32,
102        call_input_len: u32,
103        rval_ptr_ptr: u32,
104    ) -> Result<u32, FuncError>;
105
106    /// Set return value of contract execution, which is also a field in resulting receipt.
107    /// - `value_ptr` points to memory of bytes of arbitary input.
108    fn return_value(env: &T, value_ptr: u32, value_len: u32) -> Result<(), FuncError>;
109
110    /// Transfers the specified number of Grays to a specified Address
111    /// - `transfer_input_ptr` points to memory of 40 bytes address: 32-byte address and 8-byte little endian integer.
112    fn transfer(env: &T, transfer_input_ptr: u32) -> Result<(), FuncError>;
113
114    /// Insert command execution after success of this contract call.
115    /// - `create_deposit_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::CreateDeposit].
116    fn defer_create_deposit(
117        env: &T,
118        create_deposit_input_ptr: u32,
119        create_deposit_input_len: u32,
120    ) -> Result<(), FuncError>;
121
122    /// Insert command execution after success of this contract call.
123    /// - `set_deposit_settings_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::SetDepositSettings].
124    fn defer_set_deposit_settings(
125        env: &T,
126        set_deposit_settings_input_ptr: u32,
127        set_deposit_settings_input_len: u32,
128    ) -> Result<(), FuncError>;
129
130    /// Insert command execution after success of this contract call.
131    /// - `top_up_deposit_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::TopUpDeposit].
132    fn defer_topup_deposit(
133        env: &T,
134        top_up_deposit_input_ptr: u32,
135        top_up_deposit_input_len: u32,
136    ) -> Result<(), FuncError>;
137
138    /// Insert command execution after success of this contract call.
139    /// - `withdraw_deposit_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::WithdrawDeposit].
140    fn defer_withdraw_deposit(
141        env: &T,
142        withdraw_deposit_input_ptr: u32,
143        withdraw_deposit_input_len: u32,
144    ) -> Result<(), FuncError>;
145
146    /// Insert command execution after success of this contract call.
147    /// - `stake_deposit_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::StakeDeposit].
148    fn defer_stake_deposit(
149        env: &T,
150        stake_deposit_input_ptr: u32,
151        stake_deposit_input_len: u32,
152    ) -> Result<(), FuncError>;
153
154    /// Insert command execution after success of this contract call.
155    /// - `unstake_deposit_input_ptr` points to memory of arbitrary input which expects to be a serialized [pchain_types::blockchain::Command::UnstakeDeposit].
156    fn defer_unstake_deposit(
157        env: &T,
158        unstake_deposit_input_ptr: u32,
159        unstake_deposit_input_len: u32,
160    ) -> Result<(), FuncError>;
161
162    /// Add a log to the Transaction's Receipt.
163    fn log(env: &T, log_ptr: u32, log_len: u32) -> Result<(), FuncError>;
164
165    /// Computes the SHA256 digest of arbitrary input.
166    /// - returns 32 bytes digest.
167    fn sha256(env: &T, msg_ptr: u32, msg_len: u32, digest_ptr_ptr: u32) -> Result<(), FuncError>;
168
169    /// Computes the Keccak256 digest of arbitrary input.
170    /// - returns 32 bytes digest.
171    fn keccak256(env: &T, msg_ptr: u32, msg_len: u32, digest_ptr_ptr: u32)
172        -> Result<(), FuncError>;
173
174    /// Computes the RIPEMD160 digest of arbitrary input.
175    /// - returns 20 bytes digest.
176    fn ripemd(env: &T, msg_ptr: u32, msg_len: u32, digest_ptr_ptr: u32) -> Result<(), FuncError>;
177
178    /// Returns whether an Ed25519 signature was produced by a specified by a specified address over some specified message.
179    fn verify_ed25519_signature(
180        env: &T,
181        msg_ptr: u32,
182        msg_len: u32,
183        signature_ptr: u32,
184        address_ptr: u32,
185    ) -> Result<i32, FuncError>;
186}
187
188/// Create importable for instantiation of contract module.
189pub(crate) fn create_importable<'a, T, K>(store: &'a Store, env: &T) -> Importable<'a>
190where
191    T: wasmer::WasmerEnv + 'static,
192    K: ContractBinaryInterface<T> + 'static,
193{
194    Importable(
195        imports! {
196            "env" => {
197                "set" =>  Function::new_native_with_env(store, env.clone(), K::set),
198                "get" => Function::new_native_with_env(store, env.clone(), K::get),
199                "get_network_storage" => Function::new_native_with_env(store, env.clone(), K::get_network_storage),
200                "balance" => Function::new_native_with_env(store, env.clone(), K::balance),
201
202                "block_height" => Function::new_native_with_env(store, env.clone(), K::block_height),
203                "block_timestamp" => Function::new_native_with_env(store, env.clone(), K::block_timestamp),
204                "prev_block_hash" => Function::new_native_with_env(store, env.clone(), K::prev_block_hash),
205
206                "calling_account" => Function::new_native_with_env(store, env.clone(), K::calling_account),
207                "current_account" => Function::new_native_with_env(store, env.clone(), K::current_account),
208                "method" => Function::new_native_with_env(store, env.clone(), K::method),
209                "arguments" => Function::new_native_with_env(store, env.clone(), K::arguments),
210                "amount" => Function::new_native_with_env(store, env.clone(), K::amount),
211                "is_internal_call" => Function::new_native_with_env(store, env.clone(), K::is_internal_call),
212                "transaction_hash" => Function::new_native_with_env(store, env.clone(), K::transaction_hash),
213
214                "call" => Function::new_native_with_env(store, env.clone(), K::call),
215                "return_value" => Function::new_native_with_env(store, env.clone(), K::return_value),
216                "transfer" => Function::new_native_with_env(store, env.clone(), K::transfer),
217                "defer_create_deposit" => Function::new_native_with_env(store, env.clone(), K::defer_create_deposit),
218                "defer_set_deposit_settings" => Function::new_native_with_env(store, env.clone(), K::defer_set_deposit_settings),
219                "defer_topup_deposit" => Function::new_native_with_env(store, env.clone(), K::defer_topup_deposit),
220                "defer_withdraw_deposit" => Function::new_native_with_env(store, env.clone(), K::defer_withdraw_deposit),
221                "defer_stake_deposit" => Function::new_native_with_env(store, env.clone(), K::defer_stake_deposit),
222                "defer_unstake_deposit" => Function::new_native_with_env(store, env.clone(), K::defer_unstake_deposit),
223
224                "_log" => Function::new_native_with_env(store, env.clone(), K::log),
225
226                "sha256" => Function::new_native_with_env(store, env.clone(), K::sha256),
227                "keccak256" => Function::new_native_with_env(store, env.clone(), K::keccak256),
228                "ripemd" => Function::new_native_with_env(store, env.clone(), K::ripemd),
229                "verify_ed25519_signature" => Function::new_native_with_env(store, env.clone(), K::verify_ed25519_signature),
230            }
231        },
232        store,
233    )
234}
235
236/// Create importable (View) for instantiation of contract module.
237pub(crate) fn create_importable_view<'a, T, K>(store: &'a Store, env: &T) -> Importable<'a>
238where
239    T: wasmer::WasmerEnv + 'static,
240    K: ContractBinaryInterface<T> + 'static,
241{
242    Importable(
243        imports! {
244            "env" => {
245                "set" => Function::new_native(store, not_callable::set),
246                "get" => Function::new_native_with_env(store, env.clone(), K::get),
247                "get_network_storage" => Function::new_native_with_env(store, env.clone(), K::get_network_storage),
248                "balance" => Function::new_native_with_env(store, env.clone(), K::balance),
249
250                "block_height" => Function::new_native(store, not_callable::block_height),
251                "block_timestamp" => Function::new_native(store, not_callable::block_timestamp),
252                "prev_block_hash" => Function::new_native(store, not_callable::prev_block_hash),
253
254                "calling_account" => Function::new_native(store, not_callable::calling_account),
255                "current_account" => Function::new_native_with_env(store, env.clone(), K::current_account),
256                "method" => Function::new_native_with_env(store, env.clone(), K::method),
257                "arguments" => Function::new_native_with_env(store, env.clone(), K::arguments),
258                "amount" => Function::new_native(store, not_callable::amount),
259                "is_internal_call" => Function::new_native_with_env(store, env.clone(), K::is_internal_call),
260                "transaction_hash" => Function::new_native(store, not_callable::transaction_hash),
261
262                "call" => Function::new_native_with_env(store, env.clone(), K::call), // TODO
263                "return_value" => Function::new_native_with_env(store, env.clone(), K::return_value),
264                "transfer" => Function::new_native(store, not_callable::transfer),
265                "defer_create_deposit" => Function::new_native(store, not_callable::defer_create_deposit),
266                "defer_set_deposit_settings" => Function::new_native(store, not_callable::defer_set_deposit_settings),
267                "defer_topup_deposit" => Function::new_native(store, not_callable::defer_topup_deposit),
268                "defer_withdraw_deposit" => Function::new_native(store, not_callable::defer_withdraw_deposit),
269                "defer_stake_deposit" => Function::new_native(store, not_callable::defer_stake_deposit),
270                "defer_unstake_deposit" => Function::new_native(store, not_callable::defer_unstake_deposit),
271
272                "_log" => Function::new_native_with_env(store, env.clone(), K::log),
273
274                "sha256" => Function::new_native_with_env(store, env.clone(), K::sha256),
275                "keccak256" => Function::new_native_with_env(store, env.clone(), K::keccak256),
276                "ripemd" => Function::new_native_with_env(store, env.clone(), K::ripemd),
277                "verify_ed25519_signature" => Function::new_native_with_env(store, env.clone(), K::verify_ed25519_signature),
278            }
279        },
280        store,
281    )
282}
283
284/// Importable is data object required to instantiate contract module
285pub(crate) struct Importable<'a>(pub(crate) ImportObject, &'a Store);
286
287/// `blank` implementations of exports functions, used to instantiate a contract to
288/// extract its exported metadata (without executing any of its methods).
289pub(crate) mod blank {
290    use wasmer::{imports, Function, Store};
291
292    pub(crate) fn imports(store: &Store) -> wasmer::ImportObject {
293        imports! {
294            "env" => {
295                "set" => Function::new_native(store, set),
296                "get" => Function::new_native(store, get),
297                "get_network_storage" => Function::new_native(store, get_network_storage),
298                "balance" => Function::new_native(store, balance),
299
300                "block_height" => Function::new_native(store, block_height),
301                "block_timestamp" => Function::new_native(store, block_timestamp),
302                "prev_block_hash" => Function::new_native(store, prev_block_hash),
303
304                "calling_account" => Function::new_native(store, calling_account),
305                "current_account" => Function::new_native(store, current_account),
306                "method" => Function::new_native(store, method),
307                "arguments" => Function::new_native(store, arguments),
308                "amount" => Function::new_native(store, amount),
309                "is_internal_call" => Function::new_native(store, is_internal_call),
310                "transaction_hash" => Function::new_native(store, transaction_hash),
311
312                "call" => Function::new_native(store, call),
313                "return_value" => Function::new_native(store, return_value),
314                "transfer" => Function::new_native(store, transfer),
315                "defer_create_deposit" => Function::new_native(store, defer_create_deposit),
316                "defer_set_deposit_settings" => Function::new_native(store, defer_set_deposit_settings),
317                "defer_topup_deposit" => Function::new_native(store, defer_topup_deposit),
318                "defer_withdraw_deposit" => Function::new_native(store, defer_withdraw_deposit),
319                "defer_stake_deposit" => Function::new_native(store, defer_stake_deposit),
320                "defer_unstake_deposit" => Function::new_native(store, defer_unstake_deposit),
321
322                "_log" => Function::new_native(store, log),
323
324                "sha256" => Function::new_native(store, sha256),
325                "keccak256" => Function::new_native(store, keccak256),
326                "ripemd" => Function::new_native(store, ripemd),
327                "verify_ed25519_signature" => Function::new_native(store, verify_ed25519_signature),
328            }
329        }
330    }
331
332    pub(crate) fn set(_: u32, _: u32, _: u32, _: u32) {}
333    pub(crate) fn get(_: u32, _: u32, _: u32) -> i64 {
334        0
335    }
336    pub(crate) fn get_network_storage(_: u32, _: u32, _: u32) -> i64 {
337        0
338    }
339    pub(crate) fn balance() -> u64 {
340        0
341    }
342
343    pub(crate) fn block_height() -> u64 {
344        0
345    }
346    pub(crate) fn block_timestamp() -> u32 {
347        0
348    }
349    pub(crate) fn prev_block_hash(_: u32) {}
350
351    pub(crate) fn calling_account(_: u32) {}
352    pub(crate) fn current_account(_: u32) {}
353    pub(crate) fn method(_: u32) -> u32 {
354        0
355    }
356    pub(crate) fn arguments(_: u32) -> u32 {
357        0
358    }
359    pub(crate) fn amount() -> u64 {
360        0
361    }
362    pub(crate) fn is_internal_call() -> i32 {
363        0
364    }
365    pub(crate) fn transaction_hash(_: u32) {}
366
367    pub(crate) fn call(_: u32, _: u32, _: u32) -> u32 {
368        0
369    }
370    pub(crate) fn return_value(_: u32, _: u32) {}
371    pub(crate) fn transfer(_: u32) {}
372    pub(crate) fn defer_create_deposit(_: u32, _: u32) {}
373    pub(crate) fn defer_set_deposit_settings(_: u32, _: u32) {}
374    pub(crate) fn defer_topup_deposit(_: u32, _: u32) {}
375    pub(crate) fn defer_withdraw_deposit(_: u32, _: u32) {}
376    pub(crate) fn defer_stake_deposit(_: u32, _: u32) {}
377    pub(crate) fn defer_unstake_deposit(_: u32, _: u32) {}
378
379    pub(crate) fn log(_: u32, _: u32) {}
380
381    pub(crate) fn sha256(_: u32, _: u32, _: u32) {}
382    pub(crate) fn keccak256(_: u32, _: u32, _: u32) {}
383    pub(crate) fn ripemd(_: u32, _: u32, _: u32) {}
384    pub(crate) fn verify_ed25519_signature(_: u32, _: u32, _: u32, _: u32) -> i32 {
385        0
386    }
387}
388
389/// stubs that are used as non-callable host functions. E.g. set() in view calls.
390mod not_callable {
391    use super::FuncError;
392
393    pub(crate) fn set(_: u32, _: u32, _: u32, _: u32) -> Result<(), FuncError> {
394        Err(FuncError::Internal)
395    }
396
397    pub(crate) fn block_height() -> Result<u64, FuncError> {
398        Err(FuncError::Internal)
399    }
400    pub(crate) fn block_timestamp() -> Result<u32, FuncError> {
401        Err(FuncError::Internal)
402    }
403    pub(crate) fn prev_block_hash(_: u32) -> Result<(), FuncError> {
404        Err(FuncError::Internal)
405    }
406
407    pub(crate) fn calling_account(_: u32) -> Result<(), FuncError> {
408        Err(FuncError::Internal)
409    }
410    pub(crate) fn amount() -> Result<u64, FuncError> {
411        Err(FuncError::Internal)
412    }
413    pub(crate) fn transaction_hash(_: u32) -> Result<(), FuncError> {
414        Err(FuncError::Internal)
415    }
416
417    pub(crate) fn transfer(_: u32) -> Result<(), FuncError> {
418        Err(FuncError::Internal)
419    }
420    pub(crate) fn defer_create_deposit(_: u32, _: u32) -> Result<(), FuncError> {
421        Err(FuncError::Internal)
422    }
423    pub(crate) fn defer_set_deposit_settings(_: u32, _: u32) -> Result<(), FuncError> {
424        Err(FuncError::Internal)
425    }
426    pub(crate) fn defer_topup_deposit(_: u32, _: u32) -> Result<(), FuncError> {
427        Err(FuncError::Internal)
428    }
429    pub(crate) fn defer_withdraw_deposit(_: u32, _: u32) -> Result<(), FuncError> {
430        Err(FuncError::Internal)
431    }
432    pub(crate) fn defer_stake_deposit(_: u32, _: u32) -> Result<(), FuncError> {
433        Err(FuncError::Internal)
434    }
435    pub(crate) fn defer_unstake_deposit(_: u32, _: u32) -> Result<(), FuncError> {
436        Err(FuncError::Internal)
437    }
438}
439
440/// FuncError defines the error returns from execution of host functions
441#[derive(Debug, thiserror::Error)]
442pub enum FuncError {
443    #[error("Internal")]
444    Internal,
445
446    #[error("Runtime")]
447    Runtime(anyhow::Error),
448
449    #[error("GasExhaustionError")]
450    GasExhaustionError,
451
452    /// MethodCallError inside host function is the error from CtoC call.
453    #[error("Runtime")]
454    MethodCallError(MethodCallError),
455
456    /// Transaction inferred to be CtoC but no contract found with its to_address
457    #[error("ContractNotFound")]
458    ContractNotFound,
459
460    #[error("InsufficientBalance")]
461    InsufficientBalance,
462}
463
464impl From<wasmer::RuntimeError> for FuncError {
465    fn from(e: wasmer::RuntimeError) -> Self {
466        Self::Runtime(e.into())
467    }
468}
469
470impl From<anyhow::Error> for FuncError {
471    fn from(e: anyhow::Error) -> Self {
472        Self::Runtime(e)
473    }
474}