darkfi_sdk/wasm/
util.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use darkfi_serial::{Decodable, Encodable};
20use std::io::Cursor;
21
22use crate::{
23    error::{ContractError, GenericResult},
24    tx::TransactionHash,
25};
26
27/// Calls the `set_return_data` WASM function. Returns Ok(()) on success.
28/// Otherwise, convert the i64 error code into a [`ContractError`].
29pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
30    // Ensure that the number of bytes fits within the u32 data type.
31    match u32::try_from(data.len()) {
32        Ok(len) => unsafe {
33            match set_return_data_(data.as_ptr(), len) {
34                0 => Ok(()),
35                errcode => Err(ContractError::from(errcode)),
36            }
37        },
38        Err(_) => Err(ContractError::DataTooLarge),
39    }
40}
41
42/// Internal function, get raw bytes from the objects store
43pub fn get_object_bytes(data: &mut [u8], object_index: u32) -> i64 {
44    unsafe { get_object_bytes_(data.as_mut_ptr(), object_index) }
45}
46
47/// Internal function, get bytes size for an object in the store
48pub fn get_object_size(object_index: u32) -> i64 {
49    unsafe { get_object_size_(object_index) }
50}
51
52/// Auxiliary function to parse db_get return value.
53/// If either of these functions returns a negative integer error code,
54/// convert it into a [`ContractError`].
55pub(crate) fn parse_ret(ret: i64) -> GenericResult<Option<Vec<u8>>> {
56    // Negative values represent an error code.
57    if ret < 0 {
58        // However here on the special case, we'll return Ok(None)
59        if ret == crate::error::DB_GET_EMPTY {
60            return Ok(None)
61        }
62
63        return Err(ContractError::from(ret))
64    }
65
66    // Ensure that the returned value fits into the u32 datatype.
67    // Note that any negative cases should be caught by the `unimplemented`
68    // match arm above.
69    let obj = match u32::try_from(ret) {
70        Ok(obj) => obj,
71        Err(_) => return Err(ContractError::SetRetvalError),
72    };
73    let obj_size = get_object_size(obj);
74    let mut buf = vec![0u8; obj_size as usize];
75    get_object_bytes(&mut buf, obj);
76
77    Ok(Some(buf))
78}
79
80fn parse_retval_u32(ret: i64) -> GenericResult<u32> {
81    if ret < 0 {
82        return Err(ContractError::from(ret))
83    }
84    assert!(ret >= 0);
85    // This should always be possible
86    let obj = ret as u32;
87    Ok(obj)
88}
89
90/// Everyone can call this. Will return runtime configured
91/// verifying block height.
92///
93/// ```
94/// block_height = get_verifying_block_height();
95/// ```
96pub fn get_verifying_block_height() -> GenericResult<u32> {
97    let ret = unsafe { get_verifying_block_height_() };
98    parse_retval_u32(ret)
99}
100
101/// Everyone can call this. Will return runtime configured
102/// block target.
103///
104/// ```
105/// block_target = get_block_target();
106/// ```
107pub fn get_block_target() -> GenericResult<u32> {
108    let ret = unsafe { get_block_target_() };
109    parse_retval_u32(ret)
110}
111
112/// Only deploy(), metadata() and exec() can call this. Will return runtime configured
113/// transaction hash.
114///
115/// ```
116/// tx_hash = get_tx_hash();
117/// ```
118pub fn get_tx_hash() -> GenericResult<TransactionHash> {
119    let ret = unsafe { get_tx_hash_() };
120    let obj = parse_retval_u32(ret)?;
121    let mut tx_hash_data = [0u8; 32];
122    assert_eq!(get_object_size(obj), 32);
123    get_object_bytes(&mut tx_hash_data, obj);
124    Ok(TransactionHash(tx_hash_data))
125}
126
127/// Only deploy(), metadata() and exec() can call this. Will return runtime configured
128/// verifying block height.
129///
130/// ```
131/// call_idx = get_call_index();
132/// ```
133pub fn get_call_index() -> GenericResult<u8> {
134    let ret = unsafe { get_call_index_() };
135    if ret < 0 {
136        return Err(ContractError::from(ret))
137    }
138    assert!(ret >= 0);
139    // This should always be possible
140    let obj = ret as u8;
141    Ok(obj)
142}
143
144/// Everyone can call this. Will return current blockchain timestamp.
145///
146/// ```
147/// timestamp = get_blockchain_time();
148/// ```
149pub fn get_blockchain_time() -> GenericResult<Option<Vec<u8>>> {
150    let ret = unsafe { get_blockchain_time_() };
151    parse_ret(ret)
152}
153
154/// Only exec() can call this. Will return last block height.
155///
156/// ```
157/// last_block_height = get_last_block_height();
158/// ```
159pub fn get_last_block_height() -> GenericResult<Option<Vec<u8>>> {
160    let ret = unsafe { get_last_block_height_() };
161    parse_ret(ret)
162}
163
164/// Only metadata() and exec() can call this. Will return transaction
165/// bytes by provided hash.
166///
167/// ```
168/// tx_bytes = get_tx(hash);
169/// tx = deserialize(&tx_bytes)?;
170/// ```
171pub fn get_tx(hash: &TransactionHash) -> GenericResult<Option<Vec<u8>>> {
172    let mut buf = vec![];
173    hash.encode(&mut buf)?;
174
175    let ret = unsafe { get_tx_(buf.as_ptr()) };
176    parse_ret(ret)
177}
178
179/// Only metadata() and exec() can call this. Will return transaction
180/// location by provided hash.
181///
182/// ```
183/// (block_height, tx_index) = get_tx_location(hash)?;
184/// ```
185pub fn get_tx_location(hash: &TransactionHash) -> GenericResult<(u32, u16)> {
186    let mut buf = vec![];
187    hash.encode(&mut buf)?;
188
189    let ret = unsafe { get_tx_location_(buf.as_ptr()) };
190    let loc_data = parse_ret(ret)?.ok_or(ContractError::DbGetFailed)?;
191    let mut cursor = Cursor::new(loc_data);
192    Ok((Decodable::decode(&mut cursor)?, Decodable::decode(&mut cursor)?))
193}
194
195extern "C" {
196    fn set_return_data_(ptr: *const u8, len: u32) -> i64;
197    fn get_object_bytes_(ptr: *const u8, len: u32) -> i64;
198    fn get_object_size_(len: u32) -> i64;
199
200    fn get_verifying_block_height_() -> i64;
201    fn get_block_target_() -> i64;
202    fn get_tx_hash_() -> i64;
203    fn get_call_index_() -> i64;
204    fn get_blockchain_time_() -> i64;
205    fn get_last_block_height_() -> i64;
206    fn get_tx_(ptr: *const u8) -> i64;
207    fn get_tx_location_(ptr: *const u8) -> i64;
208}