gstd/msg/basic.rs
1// This file is part of Gear.
2
3// Copyright (C) 2021-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19#[cfg(not(feature = "ethexe"))]
20use crate::ReservationId;
21use crate::{
22 ActorId, MessageId,
23 msg::{self, utils},
24 prelude::{Vec, ops::RangeBounds, vec},
25};
26use gcore::errors::Result;
27use gstd_codegen::wait_for_reply;
28use scale_info::scale::Output;
29
30/// Message handle.
31///
32/// Gear allows users and program interaction via
33/// messages. Message creation consists of the following parts: message
34/// initialization, filling the message with payload (can be gradual), and
35/// message sending.
36///
37/// /// Here are the functions that constitute the parts of forming and sending
38/// messages:
39///
40/// - [`MessageHandle::init`] initializes the message
41/// - [`MessageHandle::push`] adds a payload to a message
42/// - [`MessageHandle::commit`] sends a message
43///
44/// The send transaction will be posted only after the execution of the message
45/// processing has been finished.
46///
47/// To identify a message that is being built from parts of a program, you
48/// should use `MessageHandle` obtained via [`MessageHandle::init`].
49///
50/// # Examples
51///
52/// ```
53/// use gstd::msg::{self, MessageHandle};
54///
55/// #[unsafe(no_mangle)]
56/// extern "C" fn handle() {
57/// let msg_handle = MessageHandle::init().expect("Unable to init");
58/// msg_handle.push(b"Hello,").expect("Unable to push");
59/// msg_handle.push(b" world!").expect("Unable to push");
60/// msg_handle
61/// .commit(msg::source(), 0)
62/// .expect("Unable to commit");
63/// }
64/// ```
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub struct MessageHandle(gcore::MessageHandle);
67
68impl MessageHandle {
69 /// Initialize a message to send formed in parts.
70 ///
71 /// Gear allows programs to work with messages that consist of several
72 /// parts. This function initializes a message built in parts and
73 /// returns the corresponding `MessageHandle`.
74 pub fn init() -> Result<Self> {
75 gcore::msg::send_init().map(Into::into)
76 }
77
78 /// Push a payload part of the message to be sent in parts.
79 ///
80 /// Gear allows programs to work with messages in parts.
81 /// This function adds a `payload` part to the message.
82 pub fn push<T: AsRef<[u8]>>(&self, payload: T) -> Result<()> {
83 gcore::msg::send_push(self.0, payload.as_ref())
84 }
85
86 /// Same as [`push`](Self::push) but uses the input buffer as a payload
87 /// source.
88 ///
89 /// The argument of this method is the index range defining the input
90 /// buffer's piece to be pushed back to the output.
91 ///
92 /// # Examples
93 ///
94 /// Send half of the incoming payload back to the sender.
95 ///
96 /// ```
97 /// use gstd::msg::{self, MessageHandle};
98 ///
99 /// #[unsafe(no_mangle)]
100 /// extern "C" fn handle() {
101 /// let msg_handle = MessageHandle::init().expect("Unable to init");
102 /// msg_handle
103 /// .push_input(0..msg::size() / 2)
104 /// .expect("Unable to push");
105 /// msg_handle
106 /// .commit(msg::source(), 0)
107 /// .expect("Unable to commit");
108 /// }
109 /// ```
110 pub fn push_input(&self, range: impl RangeBounds<usize>) -> Result<()> {
111 let (offset, len) = utils::decay_range(range);
112
113 gcore::msg::send_push_input(self.0, offset, len)
114 }
115
116 /// Finalize and send the message formed in parts.
117 ///
118 /// Gear allows programs to work with messages that consist of several
119 /// parts. This function finalizes the message built in parts and sends
120 /// it.
121 ///
122 /// The first argument is the address of the target account. The second
123 /// argument is the value to be transferred from the current program account
124 /// to the message target account.
125 #[wait_for_reply(self)]
126 pub fn commit(self, program: ActorId, value: u128) -> Result<MessageId> {
127 gcore::msg::send_commit(self.0, program, value)
128 }
129
130 /// Same as [`commit`](Self::commit), but sends the message after the
131 /// `delay` expressed in block count.
132 pub fn commit_delayed(self, program: ActorId, value: u128, delay: u32) -> Result<MessageId> {
133 gcore::msg::send_commit_delayed(self.0, program, value, delay)
134 }
135
136 /// Same as [`commit`](Self::commit), but with an explicit gas
137 /// limit.
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// use gstd::msg::{self, MessageHandle};
143 ///
144 /// #[unsafe(no_mangle)]
145 /// extern "C" fn handle() {
146 /// let msg_handle = MessageHandle::init().expect("Unable to init");
147 /// msg_handle.push(b"Hello,").expect("Unable to push");
148 /// msg_handle.push(b" world!").expect("Unable to push");
149 /// msg_handle
150 /// .commit_with_gas(msg::source(), 10_000_000, 42)
151 /// .expect("Unable to commit");
152 /// }
153 /// ```
154 #[cfg(not(feature = "ethexe"))]
155 #[wait_for_reply(self)]
156 pub fn commit_with_gas(
157 self,
158 program: ActorId,
159 gas_limit: u64,
160 value: u128,
161 ) -> Result<MessageId> {
162 gcore::msg::send_commit_with_gas(self.0, program, gas_limit, value)
163 }
164
165 /// Same as [`commit_with_gas`](Self::commit_with_gas), but sends
166 /// the message after the `delay` expressed in block count.
167 #[cfg(not(feature = "ethexe"))]
168 pub fn commit_with_gas_delayed(
169 self,
170 program: ActorId,
171 gas_limit: u64,
172 value: u128,
173 delay: u32,
174 ) -> Result<MessageId> {
175 gcore::msg::send_commit_with_gas_delayed(self.0, program, gas_limit, value, delay)
176 }
177
178 /// Same as [`commit`](Self::commit), but it spends gas from the
179 /// reservation instead of borrowing from the gas limit provided with the
180 /// incoming message.
181 ///
182 /// # Examples
183 ///
184 /// ```
185 /// use gstd::{
186 /// ReservationId,
187 /// msg::{self, MessageHandle},
188 /// prelude::*,
189 /// };
190 ///
191 /// #[unsafe(no_mangle)]
192 /// extern "C" fn handle() {
193 /// let reservation_id = ReservationId::reserve(5_000_000, 100).expect("Unable to reserve");
194 /// let msg_handle = MessageHandle::init().expect("Unable to init");
195 /// msg_handle.push(b"Hello,").expect("Unable to push");
196 /// msg_handle.push(b" world!").expect("Unable to push");
197 /// msg_handle
198 /// .commit_from_reservation(reservation_id, msg::source(), 42)
199 /// .expect("Unable to commit");
200 /// }
201 /// ```
202 #[cfg(not(feature = "ethexe"))]
203 #[wait_for_reply(self)]
204 pub fn commit_from_reservation(
205 self,
206 id: ReservationId,
207 program: ActorId,
208 value: u128,
209 ) -> Result<MessageId> {
210 gcore::msg::send_commit_from_reservation(id, self.into(), program, value)
211 }
212
213 /// Same as [`commit_from_reservation`](Self::commit_from_reservation), but
214 /// sends the message after the `delay` expressed in block count.
215 #[cfg(not(feature = "ethexe"))]
216 pub fn commit_delayed_from_reservation(
217 self,
218 id: ReservationId,
219 program: ActorId,
220 value: u128,
221 delay: u32,
222 ) -> Result<MessageId> {
223 gcore::msg::send_commit_delayed_from_reservation(id, self.into(), program, value, delay)
224 }
225}
226
227impl Output for MessageHandle {
228 fn write(&mut self, bytes: &[u8]) {
229 self.push(bytes).unwrap();
230 }
231}
232
233impl AsRef<gcore::MessageHandle> for MessageHandle {
234 fn as_ref(&self) -> &gcore::MessageHandle {
235 &self.0
236 }
237}
238
239impl From<MessageHandle> for gcore::MessageHandle {
240 fn from(other: MessageHandle) -> Self {
241 other.0
242 }
243}
244
245impl From<gcore::MessageHandle> for MessageHandle {
246 fn from(other: gcore::MessageHandle) -> Self {
247 Self(other)
248 }
249}
250
251/// Get a payload of the message that is currently being processed.
252///
253/// This function returns the message's payload as a byte vector.
254///
255/// # Examples
256///
257/// ```
258/// use gstd::msg;
259///
260/// #[unsafe(no_mangle)]
261/// extern "C" fn handle() {
262/// let payload = msg::load_bytes().expect("Unable to load");
263/// }
264/// ```
265///
266/// # See also
267///
268/// - [`load`](super::load) function returns a decoded payload of a custom type.
269pub fn load_bytes() -> Result<Vec<u8>> {
270 let mut result = vec![0u8; msg::size()];
271 gcore::msg::read(result.as_mut())?;
272 Ok(result)
273}
274
275/// Send a new message as a reply to the message that is currently being
276/// processed.
277///
278/// Various programs can communicate with each other, e.g., check another
279/// program's state and use it as a parameter for its business logic.
280///
281/// This function allows sending such replies, which are similar to standard
282/// messages in terms of payload and different only in how the message
283/// processing is handled by a dedicated program function called `handle_reply`.
284///
285/// The first argument is the payload buffer. The second argument is the value
286/// to be transferred from the current program account to the reply message
287/// target account.
288///
289/// Reply message transactions will be posted after processing is complete,
290/// similar to the standard message-sending function (e.g. [`send_bytes`]).
291///
292/// # Examples
293///
294/// ```
295/// use gstd::{exec, msg};
296///
297/// #[unsafe(no_mangle)]
298/// extern "C" fn handle() {
299/// msg::reply_bytes(b"PING", exec::value_available()).expect("Unable to reply");
300/// }
301/// ```
302///
303/// # See also
304///
305/// - [`reply`](super::reply) function sends a reply with an encoded payload.
306/// - [`reply_push`], [`reply_commit`] functions allow forming a reply message
307/// in parts.
308/// - [`send_bytes`] function sends a new message to the program or user.
309pub fn reply_bytes(payload: impl AsRef<[u8]>, value: u128) -> Result<MessageId> {
310 gcore::msg::reply(payload.as_ref(), value)
311}
312
313/// Same as [`reply_bytes`], but it spends gas from a reservation instead of
314/// borrowing it from the gas limit provided with the incoming message.
315///
316/// The first argument is the reservation identifier [`ReservationId`] obtained
317/// by calling the corresponding API. The second argument is the payload buffer.
318/// The last argument is the value to be transferred from the current program
319/// account to the reply message target account.
320///
321/// # Examples
322///
323/// ```
324/// use gstd::{ReservationId, msg, prelude::*};
325///
326/// #[unsafe(no_mangle)]
327/// extern "C" fn handle() {
328/// let reservation_id = ReservationId::reserve(5_000_000, 100).expect("Unable to reserve");
329/// msg::reply_from_reservation(reservation_id, b"PING", 0).unwrap();
330/// }
331/// ```
332///
333/// # See also
334///
335/// - [`send_bytes_from_reservation`] function sends a new message to the
336/// program or user by using gas from a reservation.
337#[cfg(not(feature = "ethexe"))]
338pub fn reply_bytes_from_reservation(
339 id: ReservationId,
340 payload: impl AsRef<[u8]>,
341 value: u128,
342) -> Result<MessageId> {
343 gcore::msg::reply_from_reservation(id, payload.as_ref(), value)
344}
345
346/// Same as [`reply_bytes`], but with an explicit gas limit.
347///
348/// # Examples
349///
350/// ```
351/// use gstd::{exec, msg};
352///
353/// #[unsafe(no_mangle)]
354/// extern "C" fn handle() {
355/// msg::reply_bytes_with_gas(b"PING", exec::gas_available() / 2, 0).expect("Unable to reply");
356/// }
357/// ```
358#[cfg(not(feature = "ethexe"))]
359pub fn reply_bytes_with_gas(
360 payload: impl AsRef<[u8]>,
361 gas_limit: u64,
362 value: u128,
363) -> Result<MessageId> {
364 gcore::msg::reply_with_gas(payload.as_ref(), gas_limit, value)
365}
366
367/// Finalize and send the current reply message.
368///
369/// Some programs can rely on their messages to other programs, i.e., check
370/// another program's state and use it as a parameter for its own business
371/// logic. The basic implementation is covered in [`reply`](super::reply)
372/// function.
373///
374/// This function allows sending a reply message filled with payload parts via
375/// [`reply_push`] during the message handling. The [`reply_commit`] function
376/// finalizes the reply message and sends it to the program invoker.
377///
378/// The only argument is the value to be transferred from the current program
379/// account to the reply message target account.
380///
381/// Note that an incomplete reply message will be dropped if the
382/// [`reply_commit`] function has not been called before the current execution
383/// ends.
384///
385/// # Examples
386///
387/// ```
388/// use gstd::msg;
389///
390/// #[unsafe(no_mangle)]
391/// extern "C" fn handle() {
392/// msg::reply_push(b"Hello,").expect("Unable to push");
393/// msg::reply_push(b" world!").expect("Unable to push");
394/// msg::reply_commit(42).expect("Unable to commit");
395/// }
396/// ```
397///
398/// # See also
399///
400/// - [`reply_push`] function allows forming a reply message in parts.
401/// - [`MessageHandle::commit`] function finalizes and sends a message formed in
402/// parts.
403pub fn reply_commit(value: u128) -> Result<MessageId> {
404 gcore::msg::reply_commit(value)
405}
406
407/// Same as [`reply_commit`], but it spends gas from a reservation instead of
408/// borrowing it from the gas limit provided with the incoming message.
409///
410/// # Examples
411///
412/// ```
413/// use gstd::{ReservationId, msg, prelude::*};
414///
415/// #[unsafe(no_mangle)]
416/// extern "C" fn handle() {
417/// msg::reply_push(b"Hello,").expect("Unable to push");
418/// msg::reply_push(b" world!").expect("Unable to push");
419/// let reservation_id = ReservationId::reserve(5_000_000, 100).expect("Unable to reserves");
420/// msg::reply_commit_from_reservation(reservation_id, 42).expect("Unable to commit");
421/// }
422/// ```
423///
424/// # See also
425///
426/// - [`reply_push`] function allows forming a reply message in parts.
427/// - [`ReservationId`] struct allows reserve gas for later use.
428#[cfg(not(feature = "ethexe"))]
429pub fn reply_commit_from_reservation(id: ReservationId, value: u128) -> Result<MessageId> {
430 gcore::msg::reply_commit_from_reservation(id, value)
431}
432
433/// Same as [`reply_commit`], but with an explicit gas limit.
434///
435/// # Examples
436///
437/// ```
438/// use gstd::{exec, msg};
439///
440/// #[unsafe(no_mangle)]
441/// extern "C" fn handle() {
442/// msg::reply_push(b"Hello, ").expect("Unable to push");
443/// msg::reply_push(b", world!").expect("Unable to push");
444/// msg::reply_commit_with_gas(exec::gas_available() / 2, 0).expect("Unable to commit");
445/// }
446/// ```
447///
448/// # See also
449///
450/// - [`reply_push`] function allows forming a reply message in parts.
451#[cfg(not(feature = "ethexe"))]
452pub fn reply_commit_with_gas(gas_limit: u64, value: u128) -> Result<MessageId> {
453 gcore::msg::reply_commit_with_gas(gas_limit, value)
454}
455
456/// Push a payload part to the current reply message.
457///
458/// Some programs can rely on their messages to other programs, i.e., check
459/// another program's state and use it as a parameter for its own business
460/// logic. The basic implementation is covered in the [`reply_bytes`] function.
461///
462/// This function allows filling the reply `payload` parts via [`reply_push`]
463/// during the message handling. The payload can consist of several parts.
464///
465/// Note that an incomplete reply message will be dropped if the
466/// [`reply_commit`] function has not been called before the current execution
467/// ends.
468///
469/// # Examples
470///
471/// See the [`reply_commit`] examples.
472///
473/// # See also
474///
475/// - [`reply_commit`] function finalizes and sends the current reply message.
476pub fn reply_push<T: AsRef<[u8]>>(payload: T) -> Result<()> {
477 gcore::msg::reply_push(payload.as_ref())
478}
479
480/// Same as [`reply_push`] but uses the input buffer as a payload source.
481///
482/// The argument of this method is the index range defining the input
483/// buffer's piece to be pushed back to the output.
484///
485/// # Examples
486///
487/// Send half of the incoming payload back to the sender as a reply.
488///
489/// ```
490/// use gstd::msg;
491///
492/// #[unsafe(no_mangle)]
493/// extern "C" fn handle() {
494/// msg::reply_push_input(0..msg::size() / 2).expect("Unable to push");
495/// msg::reply_commit(0).expect("Unable to commit");
496/// }
497/// ```
498///
499/// # See also
500///
501/// - [`MessageHandle::push_input`] function allows using the input buffer as a
502/// payload source for an outgoing message.
503pub fn reply_push_input(range: impl RangeBounds<usize>) -> Result<()> {
504 let (offset, len) = utils::decay_range(range);
505
506 gcore::msg::reply_push_input(offset, len)
507}
508
509/// Send a new message to the program or user.
510///
511/// Gear allows programs to communicate with each other and users via messages.
512/// For example, the [`send_bytes`] function allows sending such messages.
513///
514/// The first argument is the address of the target account ([`ActorId`]). The
515/// second argument is the payload buffer. The last argument is the value to be
516/// transferred from the current program account to the message target account.
517///
518/// Send transaction will be posted after processing is finished, similar to the
519/// reply message [`reply_bytes`].
520///
521/// # Examples
522///
523/// Send a message with value to the arbitrary address (don't repeat it in your
524/// program!):
525///
526/// ```
527/// use gstd::{ActorId, msg};
528///
529/// #[unsafe(no_mangle)]
530/// extern "C" fn handle() {
531/// // Receiver id is collected from bytes from 0 to 31
532/// let id: [u8; 32] = core::array::from_fn(|i| i as u8);
533/// msg::send_bytes(ActorId::new(id), b"HELLO", 42).expect("Unable to send");
534/// }
535/// ```
536///
537/// # See also
538///
539/// - [`reply_bytes`] function sends a new message as a reply to the message
540/// that is currently being processed.
541/// - [`MessageHandle::init`], [`MessageHandle::push`], and
542/// [`MessageHandle::commit`] functions allow forming a message to send in
543/// parts.
544#[wait_for_reply]
545pub fn send_bytes<T: AsRef<[u8]>>(program: ActorId, payload: T, value: u128) -> Result<MessageId> {
546 gcore::msg::send(program, payload.as_ref(), value)
547}
548
549/// Same as [`send_bytes`], but sends the message after the `delay` expressed in
550/// block count.
551pub fn send_bytes_delayed<T: AsRef<[u8]>>(
552 program: ActorId,
553 payload: T,
554 value: u128,
555 delay: u32,
556) -> Result<MessageId> {
557 gcore::msg::send_delayed(program, payload.as_ref(), value, delay)
558}
559
560/// Same as [`send_bytes`], but with an explicit gas limit.
561///
562/// # Examples
563///
564/// Send a message with gas limit and value to the arbitrary address (don't
565/// repeat it in your program!):
566///
567/// ```
568/// use gstd::{ActorId, msg};
569///
570/// #[unsafe(no_mangle)]
571/// extern "C" fn handle() {
572/// // Receiver id is collected from bytes from 0 to 31
573/// let id: [u8; 32] = core::array::from_fn(|i| i as u8);
574/// msg::send_bytes_with_gas(ActorId::new(id), b"HELLO", 5_000_000, 42)
575/// .expect("Unable to send");
576/// }
577/// ```
578///
579/// # See also
580///
581/// - [`reply_bytes_with_gas`] function sends a reply with an explicit gas
582/// limit.
583/// - [`MessageHandle::init`], [`MessageHandle::push`], and
584/// [`MessageHandle::commit`] functions allow forming a message to send in
585/// parts.
586#[cfg(not(feature = "ethexe"))]
587#[wait_for_reply]
588pub fn send_bytes_with_gas<T: AsRef<[u8]>>(
589 program: ActorId,
590 payload: T,
591 gas_limit: u64,
592 value: u128,
593) -> Result<MessageId> {
594 gcore::msg::send_with_gas(program, payload.as_ref(), gas_limit, value)
595}
596
597/// Same as [`send_bytes_with_gas`], but sends the message after the `delay`
598/// expressed in block count.
599#[cfg(not(feature = "ethexe"))]
600pub fn send_bytes_with_gas_delayed<T: AsRef<[u8]>>(
601 program: ActorId,
602 payload: T,
603 gas_limit: u64,
604 value: u128,
605 delay: u32,
606) -> Result<MessageId> {
607 gcore::msg::send_with_gas_delayed(program, payload.as_ref(), gas_limit, value, delay)
608}
609
610/// Same as [`send_bytes`], but it spends gas from a reservation instead of
611/// borrowing it from the gas limit provided with the incoming message.
612///
613/// The first argument is the reservation identifier [`ReservationId`] obtained
614/// by calling the corresponding API. The second argument is the address of the
615/// target account ([`ActorId`]). The third argument is the payload buffer.
616/// Finally, the last argument is the value to be transferred from the current
617/// program account to the message target account.
618///
619/// # Examples
620///
621/// Send a message with value to the sender's address:
622///
623/// ```
624/// use gstd::{ReservationId, msg, prelude::*};
625///
626/// #[unsafe(no_mangle)]
627/// extern "C" fn handle() {
628/// // Reserve 5 million of gas for 100 blocks
629/// let reservation_id = ReservationId::reserve(5_000_000, 100).expect("Unable to reserve");
630/// // Receiver id is the message source
631/// let actor_id = msg::source();
632/// msg::send_from_reservation(reservation_id, actor_id, b"HELLO", 42).expect("Unable to send");
633/// }
634/// ```
635///
636/// # See also
637///
638/// - [`reply_bytes_from_reservation`] function sends a reply to the program or
639/// user by using gas from a reservation.
640/// - [`MessageHandle::init`], [`MessageHandle::push`], and
641/// [`MessageHandle::commit`] functions allow forming a message to send in
642/// parts.
643#[cfg(not(feature = "ethexe"))]
644#[wait_for_reply]
645pub fn send_bytes_from_reservation<T: AsRef<[u8]>>(
646 id: ReservationId,
647 program: ActorId,
648 payload: T,
649 value: u128,
650) -> Result<MessageId> {
651 gcore::msg::send_from_reservation(id, program, payload.as_ref(), value)
652}
653
654/// Same as [`send_bytes_from_reservation`], but sends the message after the
655/// `delay` expressed in block count.
656#[cfg(not(feature = "ethexe"))]
657pub fn send_bytes_delayed_from_reservation<T: AsRef<[u8]>>(
658 id: ReservationId,
659 program: ActorId,
660 payload: T,
661 value: u128,
662 delay: u32,
663) -> Result<MessageId> {
664 gcore::msg::send_delayed_from_reservation(id, program, payload.as_ref(), value, delay)
665}