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}