owasm_std/
ext.rs

1//! Safe wrappers around interpreter intrinsics.
2
3use crate::types::*;
4
5/// Generic wasm error
6#[derive(Debug)]
7pub struct Error;
8
9mod eth {
10    extern "C" {
11        /// Direct/classic call. Corresponds to "CALL" opcode in EVM
12        pub fn ccall(
13            gas: i64,
14            address: *const u8,
15            val_ptr: *const u8,
16            input_ptr: *const u8,
17            input_len: u32,
18            result_ptr: *mut u8,
19            result_len: u32,
20        ) -> i32;
21
22        /// Delegate call. Corresponds to "CALLCODE" opcode in EVM
23        pub fn dcall(
24            gas: i64,
25            address: *const u8,
26            input_ptr: *const u8,
27            input_len: u32,
28            result_ptr: *mut u8,
29            result_len: u32,
30        ) -> i32;
31
32        /// Static call. Corresponds to "STACICCALL" opcode in EVM
33        pub fn scall(
34            gas: i64,
35            address: *const u8,
36            input_ptr: *const u8,
37            input_len: u32,
38            result_ptr: *mut u8,
39            result_len: u32,
40        ) -> i32;
41
42        // blockchain functions
43        pub fn address(dest: *mut u8);
44        pub fn balance(address: *const u8, dest: *mut u8);
45        pub fn blockhash(number: i64, dest: *mut u8);
46        pub fn blocknumber() -> i64;
47        pub fn coinbase(dest: *mut u8);
48        pub fn create(
49            endowment: *const u8,
50            code_ptr: *const u8,
51            code_len: u32,
52            result_ptr: *mut u8,
53        ) -> i32;
54        pub fn create2(
55            endowment: *const u8,
56            salt: *const u8,
57            code_ptr: *const u8,
58            code_len: u32,
59            result_ptr: *mut u8,
60        ) -> i32;
61        pub fn difficulty(dest: *mut u8);
62        pub fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32);
63        pub fn fetch_input(dst: *mut u8);
64        pub fn gasleft() -> i64;
65        pub fn gaslimit(dest: *mut u8);
66        pub fn input_length() -> u32;
67        pub fn origin(dest: *mut u8);
68        pub fn ret(ptr: *const u8, len: u32) -> !;
69        pub fn sender(dest: *mut u8);
70        pub fn suicide(refund: *const u8) -> !;
71        pub fn timestamp() -> i64;
72        pub fn value(dest: *mut u8);
73    }
74}
75
76extern "C" {
77    // oasis platform functions
78    fn storage_read(key: *const u8, dst: *mut u8);
79    fn storage_write(key: *const u8, src: *const u8);
80}
81
82/// Halt execution and register account for deletion.
83///
84/// Value of the current account will be tranfered to `refund` address.
85pub fn suicide(refund: &Address) -> ! {
86    unsafe {
87        eth::suicide(refund.as_ptr());
88    }
89}
90
91/// Get balance of the given account.
92///
93/// If an account is not registered in the chain yet,
94/// it is considered as an account with `balance = 0`.
95pub fn balance(address: &Address) -> U256 {
96    unsafe { fetch_u256(|x| eth::balance(address.as_ptr(), x)) }
97}
98
99/// Create a new account with the given code
100///
101/// # Errors
102///
103/// Returns [`Error`] in case contract constructor failed.
104///
105/// [`Error`]: struct.Error.html
106pub fn create(endowment: U256, code: &[u8]) -> Result<Address, Error> {
107    let mut endowment_arr = [0u8; 32];
108    endowment.to_big_endian(&mut endowment_arr);
109    let mut result = Address::zero();
110    unsafe {
111        if eth::create(
112            endowment_arr.as_ptr(),
113            code.as_ptr(),
114            code.len() as u32,
115            (&mut result).as_mut_ptr(),
116        ) == 0
117        {
118            Ok(result)
119        } else {
120            Err(Error)
121        }
122    }
123}
124
125/// Create a new account with the given code and salt.
126///
127/// # Errors
128///
129/// Returns [`Error`] in case contract constructor failed.
130///
131/// [`Error`]: struct.Error.html
132pub fn create2(endowment: U256, salt: H256, code: &[u8]) -> Result<Address, Error> {
133    let mut endowment_arr = [0u8; 32];
134    endowment.to_big_endian(&mut endowment_arr);
135    let mut result = Address::zero();
136    unsafe {
137        if eth::create2(
138            endowment_arr.as_ptr(),
139            salt.as_ptr(),
140            code.as_ptr(),
141            code.len() as u32,
142            (&mut result).as_mut_ptr(),
143        ) == 0
144        {
145            Ok(result)
146        } else {
147            Err(Error)
148        }
149    }
150}
151
152///	Message-call into an account
153///
154///	# Arguments:
155///	* `gas`- a gas limit for a call. A call execution will halt if call exceed this amount
156/// * `address` - an address of contract to send a call
157/// * `value` - a value in Wei to send with a call
158/// * `input` - a data to send with a call
159/// * `result` - a mutable reference to be filled with a result data
160///
161///	# Returns:
162///
163/// Call is succeed if it returns `Result::Ok(())`
164/// If call returns `Result::Err(Error)` it means tha call was failed due to execution halting
165pub fn call(
166    gas: u64,
167    address: &Address,
168    value: U256,
169    input: &[u8],
170    result: &mut [u8],
171) -> Result<(), Error> {
172    let mut value_arr = [0u8; 32];
173    value.to_big_endian(&mut value_arr);
174    unsafe {
175        if eth::ccall(
176            gas as i64,
177            address.as_ptr(),
178            value_arr.as_ptr(),
179            input.as_ptr(),
180            input.len() as u32,
181            result.as_mut_ptr(),
182            result.len() as u32,
183        ) == 0
184        {
185            Ok(())
186        } else {
187            Err(Error)
188        }
189    }
190}
191
192/// Like [`call`], but with code at the given `address`
193///
194/// Effectively this function is like calling current account but with
195/// different code (i.e. like `DELEGATECALL` EVM instruction).
196///
197/// [`call`]: fn.call.html
198pub fn call_code(
199    gas: u64,
200    address: &Address,
201    input: &[u8],
202    result: &mut [u8],
203) -> Result<(), Error> {
204    unsafe {
205        if eth::dcall(
206            gas as i64,
207            address.as_ptr(),
208            input.as_ptr(),
209            input.len() as u32,
210            result.as_mut_ptr(),
211            result.len() as u32,
212        ) == 0
213        {
214            Ok(())
215        } else {
216            Err(Error)
217        }
218    }
219}
220
221/// Like [`call`], but this call and any of it's subcalls are disallowed to modify any storage.
222///
223/// It will return an error in this case.
224///
225/// [`call`]: fn.call.html
226pub fn static_call(
227    gas: u64,
228    address: &Address,
229    input: &[u8],
230    result: &mut [u8],
231) -> Result<(), Error> {
232    unsafe {
233        if eth::scall(
234            gas as i64,
235            address.as_ptr(),
236            input.as_ptr(),
237            input.len() as u32,
238            result.as_mut_ptr(),
239            result.len() as u32,
240        ) == 0
241        {
242            Ok(())
243        } else {
244            Err(Error)
245        }
246    }
247}
248
249/// Returns hash of the given block or H256::zero()
250///
251/// Only works for 256 most recent blocks excluding current
252/// Returns H256::zero() in case of failure
253pub fn block_hash(block_number: u64) -> H256 {
254    let mut res = H256::zero();
255    unsafe { eth::blockhash(block_number as i64, res.as_mut_ptr()) }
256    res
257}
258
259/// Get the current block’s beneficiary address (the current miner account address)
260pub fn coinbase() -> Address {
261    unsafe { fetch_address(|x| eth::coinbase(x)) }
262}
263
264/// Get the block's timestamp
265///
266/// It can be viewed as an output of Unix's `time()` function at
267/// current block's inception.
268pub fn timestamp() -> u64 {
269    unsafe { eth::timestamp() as u64 }
270}
271
272/// Get the block's number
273///
274/// This value represents number of ancestor blocks.
275/// The genesis block has a number of zero.
276pub fn block_number() -> u64 {
277    unsafe { eth::blocknumber() as u64 }
278}
279
280/// Get the block's difficulty.
281pub fn difficulty() -> U256 {
282    unsafe { fetch_u256(|x| eth::difficulty(x)) }
283}
284
285/// Get the block's gas limit.
286pub fn gas_limit() -> U256 {
287    unsafe { fetch_u256(|x| eth::gaslimit(x)) }
288}
289
290/// Get amount of gas left.
291pub fn gas_left() -> u64 {
292    unsafe { eth::gasleft() as u64 }
293}
294
295/// Get caller address
296///
297/// This is the address of the account that is directly responsible for this execution.
298/// Use [`origin`] to get an address of external account - an original initiator of a transaction
299pub fn sender() -> Address {
300    unsafe { fetch_address(|x| eth::sender(x)) }
301}
302
303/// Get execution origination address
304///
305/// This is the sender of original transaction.
306/// It could be only external account, not a contract
307pub fn origin() -> Address {
308    unsafe { fetch_address(|x| eth::origin(x)) }
309}
310
311/// Get deposited value by the instruction/transaction responsible for this execution.
312pub fn value() -> U256 {
313    unsafe { fetch_u256(|x| eth::value(x)) }
314}
315
316/// Get address of currently executing account
317pub fn address() -> Address {
318    unsafe { fetch_address(|x| eth::address(x)) }
319}
320
321/// Creates log entry with given topics and data.
322///
323/// There could be only up to 4 topics.
324///
325/// # Panics
326///
327/// If `topics` contains more than 4 elements then this function will trap.
328pub fn log(topics: &[H256], data: &[u8]) {
329    unsafe {
330        eth::elog(
331            topics.as_ptr() as *const u8,
332            topics.len() as u32,
333            data.as_ptr(),
334            data.len() as u32,
335        );
336    }
337}
338
339/// Allocates and requests [`call`] arguments (input)
340///
341/// Input data comes either with external transaction or from [`call`] input value.
342pub fn input() -> Vec<u8> {
343    let len = unsafe { eth::input_length() };
344
345    match len {
346        0 => Vec::new(),
347        non_zero => {
348            let mut data = Vec::with_capacity(non_zero as usize);
349            unsafe {
350                data.set_len(non_zero as usize);
351                eth::fetch_input(data.as_mut_ptr());
352            }
353            data
354        }
355    }
356}
357
358/// Sets a [`call`] return value
359///
360/// Pass return data to the runtime. Runtime SHOULD trap the execution.
361///
362pub fn ret(data: &[u8]) -> ! {
363    unsafe {
364        eth::ret(data.as_ptr(), data.len() as u32);
365    }
366}
367
368/// Performs read from the storage.
369pub fn read(key: &H256) -> [u8; 32] {
370    let mut dst = [0u8; 32];
371    unsafe {
372        storage_read(key.as_ptr(), dst.as_mut_ptr());
373    }
374    dst
375}
376
377/// Performs write to the storage
378pub fn write(key: &H256, val: &[u8; 32]) {
379    unsafe {
380        storage_write(key.as_ptr(), val.as_ptr());
381    }
382}
383
384unsafe fn fetch_address<F: Fn(*mut u8)>(f: F) -> Address {
385    let mut res = Address::zero();
386    f(res.as_mut_ptr());
387    res
388}
389
390unsafe fn fetch_u256<F: Fn(*mut u8)>(f: F) -> U256 {
391    let mut res = [0u8; 32];
392    f(res.as_mut_ptr());
393    U256::from_big_endian(&res)
394}