1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// Copyright (C) Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
//! Environment for running a module.
use crate::{
buffer::PayloadSlice,
env_vars::EnvVars,
ids::{ActorId, MessageId, ReservationId},
memory::Memory,
message::{DispatchKind, HandlePacket, InitPacket, MessageContext, ReplyPacket},
pages::WasmPage,
};
use alloc::{collections::BTreeSet, string::String};
use core::fmt::Display;
use gear_core_errors::{ReplyCode, SignalCode};
use gear_wasm_instrument::syscalls::SyscallName;
use parity_scale_codec::{Decode, Encode};
use scale_decode::DecodeAsType;
use scale_encode::EncodeAsType;
use scale_info::TypeInfo;
/// External api and data for managing memory and messages,
/// use by an executing program to trigger state transition
/// in runtime.
pub trait Externalities {
/// An error issued in infallible syscall.
type UnrecoverableError;
/// An error issued in fallible syscall.
type FallibleError;
/// An error issued during allocation.
type AllocError: Display;
/// Allocate number of pages.
///
/// The resulting page number should point to `pages` consecutive memory pages.
fn alloc<Context>(
&mut self,
ctx: &mut Context,
mem: &mut impl Memory<Context>,
pages_num: u32,
) -> Result<WasmPage, Self::AllocError>;
/// Free specific page.
fn free(&mut self, page: WasmPage) -> Result<(), Self::AllocError>;
/// Free specific memory range.
fn free_range(&mut self, start: WasmPage, end: WasmPage) -> Result<(), Self::AllocError>;
/// Get environment variables currently set in the system and in the form
/// corresponded to the requested version.
fn env_vars(&self, version: u32) -> Result<EnvVars, Self::UnrecoverableError>;
/// Get the current block height.
fn block_height(&self) -> Result<u32, Self::UnrecoverableError>;
/// Get the current block timestamp.
fn block_timestamp(&self) -> Result<u64, Self::UnrecoverableError>;
/// Initialize a new incomplete message for another program and return its handle.
fn send_init(&mut self) -> Result<u32, Self::FallibleError>;
/// Push an extra buffer into message payload by handle.
fn send_push(&mut self, handle: u32, buffer: &[u8]) -> Result<(), Self::FallibleError>;
/// Complete message and send it to another program.
fn send_commit(
&mut self,
handle: u32,
msg: HandlePacket,
delay: u32,
) -> Result<MessageId, Self::FallibleError>;
/// Send message to another program.
fn send(&mut self, msg: HandlePacket, delay: u32) -> Result<MessageId, Self::FallibleError> {
let handle = self.send_init()?;
self.send_commit(handle, msg, delay)
}
/// Push the incoming message buffer into message payload by handle.
fn send_push_input(
&mut self,
handle: u32,
offset: u32,
len: u32,
) -> Result<(), Self::FallibleError>;
/// Complete message and send it to another program using gas from reservation.
fn reservation_send_commit(
&mut self,
id: ReservationId,
handle: u32,
msg: HandlePacket,
delay: u32,
) -> Result<MessageId, Self::FallibleError>;
/// Send message to another program using gas from reservation.
fn reservation_send(
&mut self,
id: ReservationId,
msg: HandlePacket,
delay: u32,
) -> Result<MessageId, Self::FallibleError> {
let handle = self.send_init()?;
self.reservation_send_commit(id, handle, msg, delay)
}
/// Push an extra buffer into reply message.
fn reply_push(&mut self, buffer: &[u8]) -> Result<(), Self::FallibleError>;
/// Complete reply message and send it to source program.
fn reply_commit(&mut self, msg: ReplyPacket) -> Result<MessageId, Self::FallibleError>;
/// Complete reply message and send it to source program from reservation.
fn reservation_reply_commit(
&mut self,
id: ReservationId,
msg: ReplyPacket,
) -> Result<MessageId, Self::FallibleError>;
/// Produce reply to the current message.
fn reply(&mut self, msg: ReplyPacket) -> Result<MessageId, Self::FallibleError> {
self.reply_commit(msg)
}
/// Produce reply to the current message from reservation.
fn reservation_reply(
&mut self,
id: ReservationId,
msg: ReplyPacket,
) -> Result<MessageId, Self::FallibleError> {
self.reservation_reply_commit(id, msg)
}
/// Get the message id of the initial message.
fn reply_to(&self) -> Result<MessageId, Self::FallibleError>;
/// Get the message id which signal issues from.
fn signal_from(&self) -> Result<MessageId, Self::FallibleError>;
/// Push the incoming message buffer into reply message.
fn reply_push_input(&mut self, offset: u32, len: u32) -> Result<(), Self::FallibleError>;
/// Get the source of the message currently being handled.
fn source(&self) -> Result<ActorId, Self::UnrecoverableError>;
/// Get the reply code if the message being processed.
fn reply_code(&self) -> Result<ReplyCode, Self::FallibleError>;
/// Get the signal code if the message being processed.
fn signal_code(&self) -> Result<SignalCode, Self::FallibleError>;
/// Get the id of the message currently being handled.
fn message_id(&self) -> Result<MessageId, Self::UnrecoverableError>;
/// Get the id of program itself
fn program_id(&self) -> Result<ActorId, Self::UnrecoverableError>;
/// Send debug message.
///
/// This should be no-op in release builds.
fn debug(&self, data: &str) -> Result<(), Self::UnrecoverableError>;
/// Get the currently handled message payload slice.
fn payload_slice(&mut self, at: u32, len: u32) -> Result<PayloadSlice, Self::FallibleError>;
/// Size of currently handled message payload.
fn size(&self) -> Result<usize, Self::UnrecoverableError>;
/// Returns a random seed for the current block with message id as a subject, along with the time in the past since when it was determinable by chain observers.
fn random(&self) -> Result<(&[u8], u32), Self::UnrecoverableError>;
/// Reserve some gas for a few blocks.
fn reserve_gas(
&mut self,
amount: u64,
duration: u32,
) -> Result<ReservationId, Self::FallibleError>;
/// Unreserve gas using reservation ID.
fn unreserve_gas(&mut self, id: ReservationId) -> Result<u64, Self::FallibleError>;
/// Do system reservation.
fn system_reserve_gas(&mut self, amount: u64) -> Result<(), Self::FallibleError>;
/// Tell how much gas is left in running context.
fn gas_available(&self) -> Result<u64, Self::UnrecoverableError>;
/// Value associated with message.
fn value(&self) -> Result<u128, Self::UnrecoverableError>;
/// Tell how much value is left in running context.
fn value_available(&self) -> Result<u128, Self::UnrecoverableError>;
/// Interrupt the program and reschedule execution for maximum.
fn wait(&mut self) -> Result<(), Self::UnrecoverableError>;
/// Interrupt the program and reschedule execution in duration.
fn wait_for(&mut self, duration: u32) -> Result<(), Self::UnrecoverableError>;
/// Interrupt the program and reschedule execution for maximum,
/// but not more than duration.
fn wait_up_to(&mut self, duration: u32) -> Result<bool, Self::UnrecoverableError>;
/// Wake the waiting message and move it to the processing queue.
fn wake(&mut self, waker_id: MessageId, delay: u32) -> Result<(), Self::FallibleError>;
/// Send init message to create a new program.
fn create_program(
&mut self,
packet: InitPacket,
delay: u32,
) -> Result<(MessageId, ActorId), Self::FallibleError>;
/// Create deposit to handle reply on given message.
fn reply_deposit(
&mut self,
message_id: MessageId,
amount: u64,
) -> Result<(), Self::FallibleError>;
/// Return the set of functions that are forbidden to be called.
fn forbidden_funcs(&self) -> &BTreeSet<SyscallName>;
/// Return the current message context.
fn msg_ctx(&self) -> &MessageContext;
}
/// Composite wait type for messages waiting.
#[derive(
Debug,
Encode,
EncodeAsType,
Decode,
DecodeAsType,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
TypeInfo,
)]
pub enum MessageWaitedType {
/// Program called `gr_wait` while executing message.
Wait,
/// Program called `gr_wait_for` while executing message.
WaitFor,
/// Program called `gr_wait_up_to` with insufficient gas for full
/// duration while executing message.
WaitUpTo,
/// Program called `gr_wait_up_to` with enough gas for full duration
/// storing while executing message.
WaitUpToFull,
}
/// Trait defining type could be used as entry point for a wasm module.
pub trait WasmEntryPoint: Sized {
/// Converting self into entry point name.
fn as_entry(&self) -> &str;
/// Converting entry point name into self object, if possible.
fn try_from_entry(entry: &str) -> Option<Self>;
/// Tries to convert self into `DispatchKind`.
fn try_into_kind(&self) -> Option<DispatchKind> {
<DispatchKind as WasmEntryPoint>::try_from_entry(self.as_entry())
}
}
impl WasmEntryPoint for String {
fn as_entry(&self) -> &str {
self
}
fn try_from_entry(entry: &str) -> Option<Self> {
Some(entry.into())
}
}
impl WasmEntryPoint for DispatchKind {
fn as_entry(&self) -> &str {
match self {
Self::Init => "init",
Self::Handle => "handle",
Self::Reply => "handle_reply",
Self::Signal => "handle_signal",
}
}
fn try_from_entry(entry: &str) -> Option<Self> {
let kind = match entry {
"init" => Self::Init,
"handle" => Self::Handle,
"handle_reply" => Self::Reply,
"handle_signal" => Self::Signal,
_ => return None,
};
Some(kind)
}
}