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 },
142 subcall::AllowAllValidator,
143 )?;
144
145 <C::Runtime as Runtime>::Core::use_tx_gas(result.gas_used)?;
148
149 let result = result.call_result;
151 match (reply, result.is_success()) {
152 (NotifyReply::OnError, false)
153 | (NotifyReply::OnSuccess, true)
154 | (NotifyReply::Always, _) => {
155 let reply = Reply::Call {
157 id,
158 result: result.into(),
159 data,
160 };
161 let mut exec_ctx = ExecutionContext::new(
162 params,
163 contract.code_info,
164 contract.instance_info,
165 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
166 CurrentState::with_env(|env| env.tx_caller_address()),
167 orig_read_only,
168 orig_call_format,
169 ctx,
170 );
171 let reply_result =
172 wasm::handle_reply::<Cfg, C>(&mut exec_ctx, contract, reply);
173 let reply_result = process_execution_result(ctx, reply_result)?;
174 let reply_result = process_execution_success::<Cfg, C>(
175 ctx,
176 params,
177 contract,
178 reply_result,
179 )?;
180
181 if !reply_result.is_empty() {
183 result_data = reply_result;
184 }
185 }
186 _ => {}
187 }
188 }
189
190 _ => return Err(Error::Unsupported),
192 }
193 }
194
195 Ok(result_data)
196}