oasis_runtime_sdk_contracts/abi/oasis/
env.rs

1//! Environment query imports.
2use oasis_contract_sdk_types::{
3    env::{AccountsQuery, AccountsResponse, QueryRequest, QueryResponse},
4    InstanceId,
5};
6use oasis_runtime_sdk::{context::Context, modules::accounts::API as _, Runtime};
7
8use super::{memory::Region, OasisV1};
9use crate::{
10    abi::{gas, ExecutionContext},
11    types::Instance,
12    Config, Error,
13};
14
15impl<Cfg: Config> OasisV1<Cfg> {
16    /// Link environment query functions.
17    pub fn link_env<C: Context>(
18        instance: &mut wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
19    ) -> Result<(), Error> {
20        // env.query(request) -> response
21        let _ = instance.link_function(
22            "env",
23            "query",
24            |ctx, query: (u32, u32)| -> Result<u32, wasm3::Trap> {
25                // Make sure function was called in valid context.
26                let ec = ctx.context.ok_or(wasm3::Trap::Abort)?;
27
28                // Charge base gas amount.
29                gas::use_gas(ctx.instance, ec.params.gas_costs.wasm_env_query_base)?;
30
31                // Decode query argument.
32                let request: QueryRequest = ctx.instance.runtime().try_with_memory(
33                    |memory| -> Result<_, wasm3::Trap> {
34                        let query = Region::from_arg(query).as_slice(&memory)?;
35                        if query.len() > ec.params.max_query_size_bytes as usize {
36                            // TODO: Consider returning a nicer error message.
37                            return Err(wasm3::Trap::Abort);
38                        }
39
40                        cbor::from_slice(query).map_err(|_| wasm3::Trap::Abort)
41                    },
42                )??;
43
44                // Dispatch query.
45                let result = dispatch_query::<C>(ec.tx_context, request);
46
47                // Create new region by calling `allocate`.
48                //
49                // This makes sure that the call context is unset to avoid any potential issues
50                // with reentrancy as attempting to re-enter one of the linked function will fail.
51                Self::serialize_and_allocate_as_ptr(ctx.instance, result).map_err(|err| err.into())
52            },
53        );
54
55        // env.address_for_instance(instance_id, dst_region)
56        let _ = instance.link_function(
57            "env",
58            "address_for_instance",
59            |ctx, request: (u64, (u32, u32))| -> Result<(), wasm3::Trap> {
60                // Make sure function was called in valid context.
61                let ec = ctx.context.ok_or(wasm3::Trap::Abort)?;
62
63                // Charge base gas amount.
64                // TODO: probably separate gas cost.
65                gas::use_gas(ctx.instance, ec.params.gas_costs.wasm_env_query_base)?;
66
67                ctx.instance
68                    .runtime()
69                    .try_with_memory(|mut memory| -> Result<_, wasm3::Trap> {
70                        let instance_id: InstanceId = request.0.into();
71                        let dst = Region::from_arg(request.1).as_slice_mut(&mut memory)?;
72
73                        let address = Instance::address_for(instance_id);
74
75                        dst.copy_from_slice(address.as_ref());
76
77                        Ok(())
78                    })?
79            },
80        );
81
82        // env.debug_print(messsage, len)
83        #[cfg(feature = "debug-utils")]
84        let _ = instance.link_function(
85            "env",
86            "debug_print",
87            |ctx, request: (u32, u32)| -> Result<(), wasm3::Trap> {
88                ctx.instance
89                    .runtime()
90                    .try_with_memory(|memory| -> Result<_, wasm3::Trap> {
91                        let msg_bytes = Region::from_arg(request).as_slice(&memory)?;
92                        if let Ok(msg) = std::str::from_utf8(msg_bytes) {
93                            eprintln!("{msg}");
94                        }
95                        Ok(())
96                    })?
97            },
98        );
99
100        Ok(())
101    }
102}
103
104/// Perform environment query dispatch.
105fn dispatch_query<C: Context>(ctx: &C, query: QueryRequest) -> QueryResponse {
106    match query {
107        // Information about the current runtime block.
108        QueryRequest::BlockInfo => QueryResponse::BlockInfo {
109            round: ctx.runtime_header().round,
110            epoch: ctx.epoch(),
111            timestamp: ctx.runtime_header().timestamp,
112        },
113
114        // Accounts API queries.
115        QueryRequest::Accounts(query) => dispatch_accounts_query::<C>(ctx, query),
116
117        _ => QueryResponse::Error {
118            module: "".to_string(),
119            code: 1,
120            message: "query not supported".to_string(),
121        },
122    }
123}
124
125/// Perform accounts API query dispatch.
126fn dispatch_accounts_query<C: Context>(_ctx: &C, query: AccountsQuery) -> QueryResponse {
127    match query {
128        AccountsQuery::Balance {
129            address,
130            denomination,
131        } => {
132            let balance =
133                <C::Runtime as Runtime>::Accounts::get_balance(address.into(), denomination.into())
134                    .unwrap_or_default();
135
136            AccountsResponse::Balance { balance }.into()
137        }
138
139        _ => QueryResponse::Error {
140            module: "".to_string(),
141            code: 1,
142            message: "query not supported".to_string(),
143        },
144    }
145}