oasis_runtime_sdk_contracts/
results.rs1use std::convert::TryInto;
3
4use oasis_contract_sdk_types::{
5 event::Event,
6 message::{Message, NotifyReply, Reply},
7 ExecutionOk,
8};
9use oasis_runtime_sdk::{
10 context::Context,
11 event::etag_for_event,
12 modules::core::API as _,
13 runtime::Runtime,
14 state::CurrentState,
15 subcall::{self, SubcallInfo},
16 types::transaction::CallerAddress,
17};
18
19use crate::{
20 abi::{ExecutionContext, ExecutionResult},
21 types::ContractEvent,
22 wasm, Config, Error, Parameters, MODULE_NAME,
23};
24
25pub(crate) fn process_execution_result<C: Context>(
27 _ctx: &C,
28 result: ExecutionResult,
29) -> Result<ExecutionOk, Error> {
30 <C::Runtime as Runtime>::Core::use_tx_gas(result.gas_used)?;
32
33 result.inner
34}
35
36pub(crate) fn process_execution_success<Cfg: Config, C: Context>(
38 ctx: &C,
39 params: &Parameters,
40 contract: &wasm::Contract<'_>,
41 result: ExecutionOk,
42) -> Result<Vec<u8>, Error> {
43 process_events(contract, result.events)?;
45 let result = process_subcalls::<Cfg, C>(ctx, params, contract, result.messages, result.data)?;
47
48 Ok(result)
49}
50
51fn process_events(contract: &wasm::Contract<'_>, events: Vec<Event>) -> Result<(), Error> {
52 CurrentState::with(|state| {
54 for event in events {
55 state.emit_event_raw(etag_for_event(
56 &if event.module.is_empty() {
57 format!("{}.{}", MODULE_NAME, contract.code_info.id.as_u64())
58 } else {
59 format!(
60 "{}.{}.{}",
61 MODULE_NAME,
62 contract.code_info.id.as_u64(),
63 event.module,
64 )
65 },
66 event.code,
67 cbor::to_value(ContractEvent {
68 id: contract.instance_info.id,
69 data: event.data,
70 }),
71 ));
72 }
73 });
74
75 Ok(())
76}
77
78fn process_subcalls<Cfg: Config, C: Context>(
79 ctx: &C,
80 params: &Parameters,
81 contract: &wasm::Contract<'_>,
82 messages: Vec<Message>,
83 data: Vec<u8>,
84) -> Result<Vec<u8>, Error> {
85 let mut result_data = data;
88
89 <C::Runtime as Runtime>::Core::use_tx_gas(
91 params
92 .gas_costs
93 .subcall_dispatch
94 .saturating_mul(messages.len() as u64),
95 )?;
96
97 let message_count = messages
99 .len()
100 .try_into()
101 .map_err(|_| Error::TooManySubcalls(u16::MAX, params.max_subcall_count))?;
102 if message_count > params.max_subcall_count {
103 return Err(Error::TooManySubcalls(
104 message_count,
105 params.max_subcall_count,
106 ));
107 }
108
109 let (orig_call_format, orig_read_only) =
111 CurrentState::with_env(|env| (env.tx_call_format(), env.is_read_only()));
112
113 for msg in messages {
115 match msg {
116 Message::Call {
117 id,
118 data,
119 reply,
120 method,
121 body,
122 max_gas,
123 } => {
124 let remaining_gas = <C::Runtime as Runtime>::Core::remaining_tx_gas();
126 let max_gas = max_gas.unwrap_or(remaining_gas);
127 let max_gas = if max_gas > remaining_gas {
128 remaining_gas
129 } else {
130 max_gas
131 };
132
133 let result = subcall::call(
134 ctx,
135 SubcallInfo {
136 caller: CallerAddress::Address(contract.instance_info.address()),
137 method,
138 body,
139 max_depth: params.max_subcall_depth,
140 max_gas,
141 read_only: orig_read_only,
142 },
143 subcall::AllowAllValidator,
144 )?;
145
146 <C::Runtime as Runtime>::Core::use_tx_gas(result.gas_used)?;
149
150 let result = result.call_result;
152 match (reply, result.is_success()) {
153 (NotifyReply::OnError, false)
154 | (NotifyReply::OnSuccess, true)
155 | (NotifyReply::Always, _) => {
156 let reply = Reply::Call {
158 id,
159 result: result.into(),
160 data,
161 };
162 let mut exec_ctx = ExecutionContext::new(
163 params,
164 contract.code_info,
165 contract.instance_info,
166 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
167 CurrentState::with_env(|env| env.tx_caller_address()),
168 orig_read_only,
169 orig_call_format,
170 ctx,
171 );
172 let reply_result =
173 wasm::handle_reply::<Cfg, C>(&mut exec_ctx, contract, reply);
174 let reply_result = process_execution_result(ctx, reply_result)?;
175 let reply_result = process_execution_success::<Cfg, C>(
176 ctx,
177 params,
178 contract,
179 reply_result,
180 )?;
181
182 if !reply_result.is_empty() {
184 result_data = reply_result;
185 }
186 }
187 _ => {}
188 }
189 }
190
191 _ => return Err(Error::Unsupported),
193 }
194 }
195
196 Ok(result_data)
197}