diff --git a/docs/03-intermediate/02-message-receiving/handle_reply.md b/docs/03-intermediate/02-message-receiving/handle_reply.md index 87d1344..5961767 100644 --- a/docs/03-intermediate/02-message-receiving/handle_reply.md +++ b/docs/03-intermediate/02-message-receiving/handle_reply.md @@ -7,35 +7,37 @@ hide_table_of_contents: true In this lesson, you will learn how a program can efficiently handle request messages. This concept will be illustrated through an example of interaction between two programs. -Before analyzing the program code in detail, it will be helpful to first present a schematic overview of how Gear programs operate: +Imagine you have dApp with specific functionality, and you need to update this application by uploading a new version to the blockchain. Consequently, the application's address on the blockchain will change. To make this transition seamless for users, it's essential to maintain a consistent address that users can interact with. This issue can be addressed by using a Proxy program. The Proxy program will act as an intermediary: it will receive messages from users, forward them to the updated version of the application, and relay responses back to the users. Each time you're updating the Target program, you just nedd to update the Target's program address in the Proxy Program. + +Before analyzing program codes in detail, it will be helpful to first present a schematic overview of how Gear programs operate: ![gif 1](../img/02/handle_reply.gif) -1. The user sends an `Action` message to Program #1, which is processed by the `handle()` function. -2. This message is then passed to Program #2. -3. Program #1 sends an `Event` message to the user, indicating that the message was successfully passed to Program #2. -4. Program #2 receives the message from Program #1, processes it, and responds. -5. Program #1 receives the reply message from Program #2 via the `handle_reply()` entry point. -6. Finally, from the `handle_reply()` function, Program #1 sends the response to the user. +1. The user sends an `Action` message to `Proxy Program`, which is processed by the `handle()` function. +2. This message is then passed to `Target Program`. +3. `Proxy Program` sends an `Event:MessageSent` message to the user, indicating that the action message was successfully passed to `Target Program`. +4. `Target Program` receives the message containing a proxied `Action` from `Proxy Program`, processes it, and replies with event corresponding to desired action. +5. `Proxy Program` receives the reply message from `Target Program` via the `handle_reply()` entry point. +6. Finally, from the `handle_reply()` function, `Proxy Program` resends received event from the `Target Program` to the user. -## First program +## Proxy program -The primary task of the first program is to communicate with the second program, requiring the following structure: +The primary task of the Proxy program is to proxy user's actions to the Target program and resend replies from the Target program back to the User. Here is the structure in the Proxy program that enables handling of single flow of interaction between user and Target program: ```rust struct Session { - second_program: ActorId, // second program address - msg_id_to_actor: (MessageId, ActorId), // tuple of message identifiers and message source address + target_program_id: ActorId, // target program address + msg_id_to_actor_id: (MessageId, ActorId), // tuple containing identifier of a message sent to a Target program and Id of a User initiated the action } ``` -The following actions and events will be necessary to simulate a dialogue between the programs: +The following actions and events will be necessary to simulate a dialogue between the user and programs: ```rust #[derive(TypeInfo, Encode, Decode)] #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] -pub enum Action { +pub enum Action { // arbitrary actions should be supported in the dApp (defined by dApp author) Hello, HowAreYou, MakeRandomNumber{ @@ -46,24 +48,24 @@ pub enum Action { #[derive(TypeInfo, Encode, Decode, Debug)] #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] -pub enum Event { +pub enum Event { // arbitrary replies to the action Hello, Fine, Number(u8), - MessageSent, + MessageSent, // event cofirming succesfull message sent from Proxy to Target } ``` -During initialization, it is necessary to pass the address of the second program. +During initialization of the Proxy program, it is necessary to pass the address of the Target program. ```rust #[no_mangle] extern "C" fn init() { - let second_program = msg::load().expect("Unable to decode Init"); + let target_program_id = msg::load().expect("Unable to decode Init"); unsafe { SESSION = Some(Session { - second_program, - msg_id_to_actor: (MessageId::zero(), ActorId::zero()), + target_program_id, + msg_id_to_actor_id: (MessageId::zero(), ActorId::zero()), }); } } @@ -72,35 +74,35 @@ extern "C" fn init() { Let's focus on processing requests in the `handle()` function: 1. Receive the message with the `msg::load()` function. -2. Send a message to the second program using `msg::send()`. +2. Send a message to the Target program using `msg::send()`. 3. An important step is to store the identifier of the message returned by `msg::send()`. This allows the `handle_reply()` function to identify which message received a response. -4. Finally, send a reply message indicating that the message was sent to the second program. +4. Finally, send a reply message indicating that the message was sent to the Target program. ```rust #[no_mangle] extern "C" fn handle() { let action: Action = msg::load().expect("Unable to decode "); let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; - let msg_id = msg::send(session.second_program, action, 0).expect("Error in sending a message"); - session.msg_id_to_actor = (msg_id, msg::source()); + let msg_id = msg::send(session.target_program_id, action, 0).expect("Error in sending a message"); + session.msg_id_to_actor_id = (msg_id, msg::source()); msg::reply(Event::MessageSent, 0).expect("Error in sending a reply"); } ``` -The Gear program utilizes the `handle_reply()` function to handle replies to messages. Let’s delve into managing the response message from the second program: +The Gear program utilizes the `handle_reply()` function to handle replies to messages. Let’s delve into processing the response message from the second program: 1. Use the `msg::reply_to()` function to retrieve the identifier of the message for which the `handle_reply()` function was invoked. 2. Ensure that the message identifier matches the identifier of the message sent from the `handle()` function. This step verifies that the response corresponds to the specific message sent earlier. -3. Finally, send a reply message to the original sender’s address. +3. Finally, resend a message content from the Target program to the original sender’s address. -**It is crucial to note that calling `msg::reply()` inside the `handle_reply()` function is not permitted.** +**It is crucial to note that calling `msg::reply()` inside the `handle_reply()` function is not permitted. Instead, use `msg::send()` to proxy reply from the Target program to a User** ```rust #[no_mangle] extern "C" fn handle_reply() { let reply_message_id = msg::reply_to().expect("Failed to query reply_to data"); let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; - let (msg_id, actor) = session.msg_id_to_actor; + let (msg_id, actor) = session.msg_id_to_actor_id; if reply_message_id == msg_id { let reply: Event = msg::load().expect("Unable to decode "); msg::send(actor, reply, 0).expect("Error in sending a message"); @@ -109,12 +111,12 @@ extern "C" fn handle_reply() { ``` Just a reminder: the sender of the message will receive two messages: -- The first message, originating from the `handle()` function, indicates that the message has been forwarded to the second program. -- The second message, sent by the `handle_reply()` function, contains the response from the second program. +- The first message, originating from the `handle()` function, indicates that the original message with action has been succesfully forwarded to the Target program. +- The second message, sent by the `handle_reply()` function, contains the response from the Target program. -## Second Program +## Target Program -The first program is straightforward; it can accept various types of actions and respond with corresponding events. These responses can range from simple replies, such as `Action::HowAreYou` and `Event::Fine`, to more complex logic, such as generating a random number. +The Target program is straightforward; it can accept various types of actions and respond with corresponding events. These responses can range from simple replies, such as `Action::HowAreYou` and `Event::Fine`, to more complex logic, such as generating a random number. ```rust #![no_std] diff --git a/docs/03-intermediate/02-message-receiving/testing_handle_reply.md b/docs/03-intermediate/02-message-receiving/testing_handle_reply.md index 38029e6..f22adad 100644 --- a/docs/03-intermediate/02-message-receiving/testing_handle_reply.md +++ b/docs/03-intermediate/02-message-receiving/testing_handle_reply.md @@ -12,25 +12,25 @@ Let's verify the functionality of the programs discussed in the preceding sectio let system = System::new(); ``` -2. Retrieve the first program from the root crate using the provided `system` and the second program instance from the wasm file. +2. Retrieve the Proxy program from the root crate using the provided `system` and the Target program instance from the wasm file. ```rust -let first_program = Program::current(&system); -let second_program = Program::from_file(&system, "target/wasm32-unknown-unknown/release/second_program.opt.wasm"); +let proxy_program = Program::current(&system); +let target_program = Program::from_file(&system, "target/wasm32-unknown-unknown/release/target_program.opt.wasm"); ``` -3. Initialize the second program by sending an empty message, and then initialize the first program by passing the address of the second program to it. +3. Initialize the Target program by sending an empty message, and then initialize the Proxy program by passing the address of the Target program to it. ```rust -let result = second_program.send_bytes(USER, []); -let second_program_address: ActorId = SECOND_PROGRAM_ADDRESS.into(); -let res = first_program.send(USER, second_program_address); +let result = target_program.send_bytes(USER, []); // initialize Target program +let target_program_address: ActorId = TARGET_PROGRAM_ADDRESS.into(); +let res = proxy_program.send(USER, target_program_address); // initialize Proxy program ``` -4. Send a message with `Action::MakeRandomNumber { range: 1 }` to the first program and check for the response `Event::MessageSent`, indicating that the message was successfully sent to the second program's address. +4. Send a message with `Action::MakeRandomNumber { range: 1 }` to the Proxy program and check for the response `Event::MessageSent`, indicating that the message was successfully sent to the Target program's address. ```rust -let result = first_program.send(USER, Action::MakeRandomNumber {range: 1}); +let result = proxy_program.send(USER, Action::MakeRandomNumber {range: 1}); let log = Log::builder() .source(1) .dest(3) @@ -58,39 +58,39 @@ use gtest::{Log, Program, System}; use handle_reply_io::{Action, Event}; const USER: u64 = 3; -const SECOND_PROGRAM_ADDRESS: u64 = 2; +const TARGET_PROGRAM_ADDRESS: u64 = 2; #[test] fn success_test() { // Create a new testing environment. let system = System::new(); - // Get first program of the root crate with provided system. - let first_program = Program::current(&system); - // Get second program - let second_program = Program::from_file(&system, "target/wasm32-unknown-unknown/release/second_program.opt.wasm"); - // The second program is initialized with an empty payload message - let result = second_program.send_bytes(USER, []); + // Get proxy program of the root crate with provided system. + let proxy_program = Program::current(&system); + // Get target program + let target_program = Program::from_file(&system, "target/wasm32-unknown-unknown/release/target_program.opt.wasm"); + // The target program is initialized with an empty payload message + let result = target_program.send_bytes(USER, []); assert!(!result.main_failed()); - let second_program_address: ActorId = SECOND_PROGRAM_ADDRESS.into(); - // The first program is initialized using second_program in the payload message - let res = first_program.send(USER, second_program_address); + let target_program_address: ActorId = TARGET_PROGRAM_ADDRESS.into(); + // The proxy program is initialized using target_program in the payload message + let res = proxy_program.send(USER, target_program_address); assert!(!res.main_failed()); // Send with the message we want to receive back - let result = first_program.send(USER, Action::MakeRandomNumber {range: 1}); + let result = proxy_program.send(USER, Action::MakeRandomNumber {range: 1}); assert!(!result.main_failed()); - // check that the first message has arrived, - // which means that the message was successfully sent to the second program + // check that the proxy message has arrived, + // which means that the message was successfully sent to the target program let log = Log::builder() .source(1) .dest(3) .payload(Event::MessageSent); assert!(result.contains(&log)); - // check that the second message has arrived at the mailbox, + // check that the target message has arrived at the mailbox, // which means that a reply has been received. let mailbox = system.get_mailbox(USER); let log = Log::builder() diff --git a/docs/03-intermediate/03-wait-wake-system/handle-reply-wait-wake.md b/docs/03-intermediate/03-wait-wake-system/handle-reply-wait-wake.md index 684ab20..5db34a5 100644 --- a/docs/03-intermediate/03-wait-wake-system/handle-reply-wait-wake.md +++ b/docs/03-intermediate/03-wait-wake-system/handle-reply-wait-wake.md @@ -11,50 +11,50 @@ Now, let's apply understanding of the `exec::wait()`/`exec::wake()` functions to The user will now receive a single reply at the end of the entire process, instead of two separate messages. -## First Program +## Proxy Program -Since the second program remains unchanged, let's take a closer look at the changes in the first program: +Since the Target program remains unchanged, let's take a closer look at the changes in the Proxy program: ```rust -type MessageSentId = MessageId; +type SentMessageId = MessageId; type OriginalMessageId = MessageId; struct Session { - second_program: ActorId, - msg_ids: (MessageSentId, OriginalMessageId), - message_status: MessageStatus, + target_program_id: ActorId, + msg_ids: (SentMessageId, OriginalMessageId), + session_status: SessionStatus, } ``` New fields have been introduced: -- `msg_ids` — a tuple consisting of two elements: `MessageSentId` and `OriginalMessageId`; - - `MessageSentId` is the identifier of the message sent to the second program's address. - - `OriginalMessageId` is the identifier of the message sent to the first program (required for using the `wake()` function). -- `message_status` - the session status (required to track the stages of session activity). +- `msg_ids` — a tuple consisting of two elements: `SentMessageId` and `OriginalMessageId`; + - `SentMessageId` is the identifier of the message sent to the target program's address. + - `OriginalMessageId` is the identifier of the message sent to the proxy program by user (required for using the `wake()` function). +- `session_status` - the session status (required to track the stages of session activity). ```rust -enum MessageStatus { +enum SessionStatus { Waiting, - Sent, - Received(Event), + MessageSent, + ReplyReceived(Event), } ``` - `Waiting` — the session is in a waiting state. -- `Sent` - the intermediate session state in which the message was sent to the second program, but the response has not yet been received. -- `Received(String)` - the session state when the reply message has been received. +- `MessageSent` - the intermediate session state in which the message was sent to the target program, but the response has not yet been received. +- `ReplyReceived(String)` - the session state when the reply message has been received. With these new fields in the program structure, initialization is as follows: ```rust #[no_mangle] extern "C" fn init() { - let second_program = msg::load().expect("Unable to decode Init"); + let target_program_id = msg::load().expect("Unable to decode Init"); unsafe { SESSION = Some(Session { - second_program, + target_program_id, msg_ids: (MessageId::zero(), MessageId::zero()), - message_status: MessageStatus::Waiting, + session_status: SessionStatus::Waiting, }); } } @@ -71,33 +71,33 @@ extern "C" fn handle() { debug!("Message payload: {:?}", action); let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; - // match message_status - match &session.message_status { - MessageStatus::Waiting => { - debug!("HANDLE: MessageStatus::Waiting"); - let msg_id = msg::send(session.second_program, action, 0) + // match session_status + match &session.session_status { + SessionStatus::Waiting => { + debug!("HANDLE: SessionStatus::Waiting"); + let msg_id = msg::send(session.target_program_id, action, 0) .expect("Error in sending a message"); - debug!("HANDLE: MessageStatus::Sent"); - session.message_status = MessageStatus::Sent; + debug!("HANDLE: SessionStatus::Sent"); + session.session_status = SessionStatus::MessageSent; session.msg_ids = (msg_id, msg::id()); debug!("HANDLE: WAIT"); exec::wait(); } - MessageStatus::Sent => { - debug!("HANDLE: MessageStatus::Sent"); + SessionStatus::MessageSent => { + debug!("HANDLE: SessionStatus::MessageSent"); msg::reply(Event::MessageAlreadySent, 0).expect("Error in sending a reply"); } - MessageStatus::Received(reply_message) => { - debug!("HANDLE: MessageStatus::Received({:?})", reply_message); + SessionStatus::ReplyReceived(reply_message) => { + debug!("HANDLE: SessionStatus::ReplyReceived({:?})", reply_message); msg::reply(reply_message, 0).expect("Error in sending a reply"); - session.message_status = MessageStatus::Waiting; + session.session_status = SessionStatus::Waiting; } } debug!("HANDLE: END"); } ``` -Initially, the session is in `MessageStatus::Waiting` state. Upon a match, the code switches to the first option. The program sends a message, sets the session status to `MessageStatus::Sent`, and records the identifiers of the current and sent messages. Then, `exec::wait()` is called, pausing message processing and adding the current message to the waiting list until `exec::wake(message_id)` is called or the gas runs out. The ID of the waking message is crucial, hence `msg::id()` is stored in `session.msg_ids`. +Initially, the session is in `SessionStatus::Waiting` state. Upon a match, the code switches to the proxy option. The program sends a message, sets the session status to `SessionStatus::MessageSent`, and records the identifiers of the current message from user being processed and message sent to target program. Then, `exec::wait()` is called, pausing message processing and adding the current message to the waiting list until `exec::wake(message_id)` is called or the gas runs out. The ID of the waking message is crucial, hence `msg::id()` is stored in `session.msg_ids`. Moving to the `handle_reply()` function: @@ -108,10 +108,10 @@ extern "C" fn handle_reply() { let reply_to = msg::reply_to().expect("Failed to query reply_to data"); let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; - if reply_to == session.msg_ids.0 && session.message_status == MessageStatus::Sent { + if reply_to == session.msg_ids.0 && session.session_status == SessionStatus::MessageSent { let reply_message: Event = msg::load().expect("Unable to decode `Event`"); - debug!("HANDLE_REPLY: MessageStatus::Received {:?}", reply_message); - session.message_status = MessageStatus::Received(reply_message); + debug!("HANDLE_REPLY: SessionStatus::ReplyReceived {:?}", reply_message); + session.session_status = SessionStatus::ReplyReceived(reply_message); let original_message_id = session.msg_ids.1; debug!("HANDLE: WAKE"); exec::wake(original_message_id).expect("Failed to wake message"); @@ -119,23 +119,23 @@ extern "C" fn handle_reply() { } ``` -The condition `if reply_to == session.msg_ids.0 && session.message_status == MessageStatus::Sent` ensures the expected message has arrived at the right moment, i.e., when the session is in the correct status. The status is then set to `MessageStatus::Received(reply_message)`, and the reply message is saved. The ID of the original message is retrieved, and the `exec::wake()` function is called. This function takes the message from the waiting list, and the suspended message resumes in the `handle()` function. +The condition `if reply_to == session.msg_ids.0 && session.session_status == SessionStatus::MessageSent` ensures the expected message has arrived at the right moment, i.e., when the session is in the correct status. The status is then set to `SessionStatus::ReplyReceived(reply_message)`, and the reply message is saved. The ID of the original message is retrieved, and the `exec::wake()` function is called. This function takes the message from the waiting list, and the suspended message resumes processing in the `handle()` function. *Important note*: When `exec::wake()` is called, and the message returns to the `handle()` entry point, processing starts from the beginning. The program enters the `match` again: ```rust // ... -match &session.message_status { +match &session.session_status { // ... - MessageStatus::Received(reply_message) => { - debug!("HANDLE: MessageStatus::Received({:?})", reply_message); + SessionStatus::ReplyReceived(reply_message) => { + debug!("HANDLE: SessionStatus::ReplyReceived({:?})", reply_message); msg::reply(reply_message, 0).expect("Error in sending a reply"); - session.message_status = MessageStatus::Waiting; + session.session_status = SessionStatus::Waiting; } // ... ``` -However, this time, it proceeds to the third variant, sends a response, and sets the status to `MessageStatus::Waiting`. +However, this time, it proceeds to the third option acoordingly to the session state, sends a reply from Target program to user, and sets the status to `SessionStatus::Waiting`. Now, let's review this process as a whole: diff --git a/docs/03-intermediate/03-wait-wake-system/testing_wait_for.md b/docs/03-intermediate/03-wait-wake-system/testing_wait_for.md index dbf6737..ba2f50d 100644 --- a/docs/03-intermediate/03-wait-wake-system/testing_wait_for.md +++ b/docs/03-intermediate/03-wait-wake-system/testing_wait_for.md @@ -13,26 +13,27 @@ use gtest::{Log, Program, System}; use wait_for_io::{Event, Action}; const USER: u64 = 3; -const SECOND_PROGRAM_ADDRESS: u64 = 2; +const TARGET_PROGRAM_ADDRESS: u64 = 2; #[test] fn test() { let system = System::new(); system.init_logger(); - let first_program = Program::current(&system); - let second_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/second_program.opt.wasm"); + let proxy_program = Program::current(&system); + let target_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/target_program.opt.wasm"); - let result = second_program.send_bytes(USER, []); + let result = target_program.send_bytes(USER, []); assert!(!result.main_failed()); - let second_program_address: ActorId = SECOND_PROGRAM_ADDRESS.into(); - let result = first_program.send(USER, second_program_address); + let target_program_address: ActorId = TARGET_PROGRAM_ADDRESS.into(); + let result = proxy_program.send(USER, target_program_address); assert!(!result.main_failed()); - let result = first_program.send(USER, Action::MakeRandomNumber{range: 1}); + let result = proxy_program.send(USER, Action::MakeRandomNumber{range: 1}); assert!(!result.main_failed()); - let result = first_program.send(USER, Action::MakeRandomNumber{range: 1}); + // user attempts to send another message to a proxy program while it is still processing the first message. It is expected that the proxy program reply with event `MessageAlreadySent`. + let result = proxy_program.send(USER, Action::MakeRandomNumber{range: 1}); let log = Log::builder() .source(1) .dest(3) @@ -56,8 +57,8 @@ When running the test, observe the following debug messages carefully to verify [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message ID: MessageId([15, 200, 69, 247, 219, 197, 228, 169, 112, 34, 221, 58, 40, 159, 140, 193, 139, 19, 23, 77, 44, 107, 107, 94, 184, 209, 74, 155, 13, 80, 206, 217]) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message payload: MakeRandomNumber { range: 1 } -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Waiting -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Sent +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::Waiting +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::MessageSent [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: WAIT [DEBUG test] [handle(0xd560..12fb)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0xd560..12fb)] 0x0100..0000: Message ID: MessageId([213, 96, 108, 153, 11, 175, 246, 203, 166, 249, 165, 69, 253, 140, 44, 138, 82, 194, 230, 50, 196, 117, 66, 218, 223, 197, 172, 150, 125, 82, 18, 251]) @@ -68,6 +69,6 @@ When running the test, observe the following debug messages carefully to verify [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message ID: MessageId([15, 200, 69, 247, 219, 197, 228, 169, 112, 34, 221, 58, 40, 159, 140, 193, 139, 19, 23, 77, 44, 107, 107, 94, 184, 209, 74, 155, 13, 80, 206, 217]) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message payload: MakeRandomNumber { range: 1 } [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: No response was received -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Waiting +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::Waiting [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: END ``` diff --git a/docs/03-intermediate/03-wait-wake-system/testing_wait_wake.md b/docs/03-intermediate/03-wait-wake-system/testing_wait_wake.md index fb164ba..21c3181 100644 --- a/docs/03-intermediate/03-wait-wake-system/testing_wait_wake.md +++ b/docs/03-intermediate/03-wait-wake-system/testing_wait_wake.md @@ -13,24 +13,24 @@ use gtest::{Log, Program, System}; use wait_wake_io::{Action, Event}; const USER: u64 = 3; -const SECOND_PROGRAM_ADDRESS: u64 = 2; +const TARGET_PROGRAM_ADDRESS: u64 = 2; #[test] fn test() { let system = System::new(); system.init_logger(); - let first_program = Program::current(&system); - let second_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/second_program.opt.wasm"); + let proxy_program = Program::current(&system); + let target_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/target_program.opt.wasm"); - let result = second_program.send_bytes(USER, []); + let result = target_program.send_bytes(USER, []); assert!(!result.main_failed()); - let second_program_address: ActorId = SECOND_PROGRAM_ADDRESS.into(); - let result = first_program.send(USER, second_program_address); + let target_program_address: ActorId = TARGET_PROGRAM_ADDRESS.into(); + let result = proxy_program.send(USER, target_program_address); assert!(!result.main_failed()); - let result = first_program.send(USER, Action::MakeRandomNumber{range: 1}); + let result = proxy_program.send(USER, Action::MakeRandomNumber{range: 1}); assert!(!result.main_failed()); let log = Log::builder() .source(1) @@ -47,15 +47,15 @@ When you run the test, pay close attention to the following debug messages. Care [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message ID: MessageId([15, 200, 69, 247, 219, 197, 228, 169, 112, 34, 221, 58, 40, 159, 140, 193, 139, 19, 23, 77, 44, 107, 107, 94, 184, 209, 74, 155, 13, 80, 206, 217]) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message payload: MakeRandomNumber { range: 1 } -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Waiting -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Sent +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::Waiting +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::Sent [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: WAIT [DEBUG test] [handle(0x0547..16ea)] 0x0100..0000: HANDLE_REPLY -[DEBUG test] [handle(0x0547..16ea)] 0x0100..0000: HANDLE_REPLY: MessageStatus::Received Number(0) +[DEBUG test] [handle(0x0547..16ea)] 0x0100..0000: HANDLE_REPLY: SessionStatus::ReplyReceived Number(0) [DEBUG test] [handle(0x0547..16ea)] 0x0100..0000: HANDLE: WAKE [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message ID: MessageId([15, 200, 69, 247, 219, 197, 228, 169, 112, 34, 221, 58, 40, 159, 140, 193, 139, 19, 23, 77, 44, 107, 107, 94, 184, 209, 74, 155, 13, 80, 206, 217]) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message payload: MakeRandomNumber { range: 1 } -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Received(Number(0)) +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::ReplyReceived(Number(0)) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: END ``` diff --git a/docs/03-intermediate/03-wait-wake-system/wait_for.md b/docs/03-intermediate/03-wait-wake-system/wait_for.md index b5a4b19..35e2fe3 100644 --- a/docs/03-intermediate/03-wait-wake-system/wait_for.md +++ b/docs/03-intermediate/03-wait-wake-system/wait_for.md @@ -5,16 +5,16 @@ hide_table_of_contents: true # Breakdowns in Communication -Sometimes, the second program may not respond for various reasons, and it's crucial to handle such scenarios appropriately. +Sometimes, the Target program may not respond for various reasons, and it's crucial to handle such scenarios appropriately. ## wait_for() To address this issue, let's use `wait_for(DURATION_IN_BLOCKS)`. *Reminder: This function automatically wakes up the message from the waiting list after a specified number of blocks if `exec::wake()` has not been called.* -## Second Program +## Target Program -Add `exec::wait()` to the second program so that it does not reply to incoming messages. +Add `exec::wait()` to the Target program so that it does not reply to incoming messages. ```rust #[no_mangle] @@ -25,9 +25,9 @@ extern "C" fn handle() { ``` -## First Program +## Proxy Program -In the first program, the `handle()` function will be modified. Instead of `exec::wait()`, let's use `exec::wait_for(3)`. This ensures that if `exec::wake()` in `handle_reply()` is not called within 3 blocks, message processing will automatically resume. +In the Proxy program, the `handle()` function will be modified. Instead of `exec::wait()`, let's use `exec::wait_for(3)`. This ensures that if `exec::wake()` in `handle_reply()` is not called within 3 blocks, message processing will automatically resume. ```rust #[no_mangle] @@ -38,39 +38,39 @@ extern "C" fn handle() { debug!("Message payload: {:?}", action); let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; - // match message_status - match &session.message_status { - MessageStatus::Received(reply_message) => { - debug!("HANDLE: MessageStatus::Received"); - msg::reply(reply_message, 0).expect("Error in sending a reply"); - debug!("HANDLE: MessageStatus::Waiting"); - session.message_status = MessageStatus::Waiting; - } - MessageStatus::Waiting => { - debug!("HANDLE: MessageStatus::Waiting"); - let msg_id = msg::send(session.second_program, action, 0) + // match session_status + match &session.session_status { + SessionStatus::Waiting => { + debug!("HANDLE: SessionStatus::Waiting"); + let msg_id = msg::send(session.target_program_id, action, 0) .expect("Error in sending a message"); - debug!("HANDLE: MessageStatus::Sent"); - session.message_status = MessageStatus::Sent; + debug!("HANDLE: SessionStatus::MessageSent"); + session.session_status = SessionStatus::MessageSent; session.msg_ids = (msg_id, msg::id()); debug!("HANDLE: WAIT"); exec::wait_for(3); } - MessageStatus::Sent => { + SessionStatus::MessageSent => { if msg::id() == session.msg_ids.1 { debug!("HANDLE: No response was received"); msg::reply(Event::NoReplyReceived, 0).expect("Error in sending a reply"); - debug!("HANDLE: MessageStatus::Waiting"); - session.message_status = MessageStatus::Waiting; + debug!("HANDLE: SessionStatus::Waiting"); + session.session_status = SessionStatus::Waiting; } else { debug!("HANDLE: Event::MessageAlreadySent"); msg::reply(Event::MessageAlreadySent, 0).expect("Error in sending a reply"); } } + SessionStatus::ReplyReceived(reply_message) => { + debug!("HANDLE: SessionStatus::ReplyReceived"); + msg::reply(reply_message, 0).expect("Error in sending a reply"); + debug!("HANDLE: SessionStatus::Waiting"); + session.session_status = SessionStatus::Waiting; + } } debug!("HANDLE: END"); } ``` -In such a case, the program will return to `handle()`, but this time with the session status as `MessageStatus::Sent`. Consequently, a message will be sent to the user indicating the absence of a response, and the status will be updated to `MessageStatus::Waiting`. \ No newline at end of file +In such a case, the program will return to `handle()`, but this time with the session status as `SessionStatus::Sent`. Consequently, a message will be sent to the user indicating the absence of a response, and the status will be updated to `SessionStatus::Waiting`. diff --git a/docs/03-intermediate/03-wait-wake-system/wait_wake.md b/docs/03-intermediate/03-wait-wake-system/wait_wake.md index 3d0d9a7..9742daa 100644 --- a/docs/03-intermediate/03-wait-wake-system/wait_wake.md +++ b/docs/03-intermediate/03-wait-wake-system/wait_wake.md @@ -9,7 +9,7 @@ To enhance message handling in Gear programs, the `exec::wait()` and `exec::wake ## Wait -`exec::wait()`: This function pauses the current message handling process, completing it with a special result and placing the message into the waiting queue. The message can later be reactivated using the `exec::wake()` function. When a message is put into the waiting state with `exec::wait()`, all changes made by the program before the wait call are preserved. These changes are stored in the program's persistent storage, ensuring they are not lost when message handling is paused. While in the waiting state, all remaining gas is allocated to the message in the waiting queue. If the gas is depleted while the message is waiting, it will not be awakened, potentially causing the program to become stuck in an intermediate state. It is essential to note that each block of a message's presence in the queue incurs a gas charge. +`exec::wait()`: This function pauses the current message handling process, completing it with a special result and placing the message into the waiting queue. The message can later be reactivated using the `exec::wake()` function. When a message is put into the waiting queue with `exec::wait()`, all changes made by the program before the wait call are preserved. These changes are stored in the program's persistent storage, ensuring they are not lost when message handling is paused. All remaining gas associated with the message stays allocated to the message in the waiting queue. While in the waiting state, the newtork charges gas to keep the message in the waiting queue. If all associated gas is depleted, the message will not be awakened, potentially causing the program to become stuck in an intermediate state. It is essential to note that each block of a message's presence in the queue incurs a gas charge. To address this, Gear provides functions for entering a waiting state for a specified number of blocks: @@ -18,6 +18,6 @@ To address this, Gear provides functions for entering a waiting state for a spec - `exec::wait_up_to(duration)`: Allows the message to wait for a period, but only for as many blocks as it has gas to afford. This ensures that the wait does not exceed the available resources. ## Wake -`exec::wake()`: Resumes the execution of a message that was previously paused with the `exec::wait()` function. Calling the wake function with a valid message ID takes the message out of the waiting queue and places it back in the processing queue. +`exec::wake(message_id)`: Resumes the execution of a message that was previously paused with the `exec::wait()`, `exec::wait_for()`, `exec::wait_up_to()` functions. Calling the wake function with a valid message ID takes the message out of the waiting queue and places it back in the processing queue. **Important: Message execution restarts from the beginning. The message enters at the `handle` entry point and executes all logic from the start.** \ No newline at end of file diff --git a/docs/03-intermediate/04-delayed-message/delayed-message-example.md b/docs/03-intermediate/04-delayed-message/delayed-message-example.md index c6fd326..2acae41 100644 --- a/docs/03-intermediate/04-delayed-message/delayed-message-example.md +++ b/docs/03-intermediate/04-delayed-message/delayed-message-example.md @@ -14,7 +14,7 @@ In this scenario, there are two types of messages: #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] pub enum Action{ - SendMessage(MessageAction), // action to send a message to the second program + SendMessage(MessageAction), // action to send a message to the target program CheckReply, // action to check for a response } @@ -47,9 +47,9 @@ Add `ActorId` to `msg_ids` to store the address of the message sender. ```rust struct Session { - second_program: ActorId, + target_program: ActorId, msg_ids: (MessageSentId, OriginalMessageId, ActorId), - message_status: MessageStatus, + session_status: SessionStatus, } ``` @@ -58,18 +58,18 @@ The initialization will then proceed as follows: ```rust #[no_mangle] extern "C" fn init() { - let second_program = msg::load().expect("Unable to decode Init"); + let target_program = msg::load().expect("Unable to decode Init"); unsafe { SESSION = Some(Session { - second_program, + target_program, msg_ids: (MessageId::zero(), MessageId::zero(), ActorId::zero()), - message_status: MessageStatus::Waiting, + session_status: SessionStatus::Waiting, }); } } ``` -After sending the message to the second program, send a delayed message using `msg::send_delayed(exec::program_id(), Action::CheckReply, 0, 3)`, setting a delay of three blocks. +After sending the message to the target program, proxy program sends a delayed message to itself using `msg::send_delayed(exec::program_id(), Action::CheckReply, 0, 3)`, setting a delay of three blocks. ```rust #[no_mangle] @@ -82,13 +82,13 @@ extern "C" fn handle() { match action { Action::SendMessage(message_action) => { - if session.message_status == MessageStatus::Waiting { - debug!("HANDLE: Action::SendMessage and MessageStatus::Waiting"); - let msg_id = msg::send(session.second_program, message_action, 0) + if session.session_status == SessionStatus::Waiting { + debug!("HANDLE: Action::SendMessage and SessionStatus::Waiting"); + let msg_id = msg::send(session.target_program, message_action, 0) .expect("Error in sending a message"); - debug!("HANDLE: MessageStatus::Sent"); - session.message_status = MessageStatus::Sent; + debug!("HANDLE: SessionStatus::MessageSent"); + session.session_status = SessionStatus::MessageSent; session.msg_ids = (msg_id, msg::id(), msg::source()); msg::send_delayed(exec::program_id(), Action::CheckReply, 0, 3) @@ -103,17 +103,33 @@ extern "C" fn handle() { } Action::CheckReply => { debug!("HANDLE: Action::CheckReply"); - if session.message_status == MessageStatus::Sent && msg::source() == exec::program_id() { + if session.session_status == SessionStatus::MessageSent && msg::source() == exec::program_id() { debug!("HANDLE: No response was received"); msg::send(session.msg_ids.2, Event::NoReplyReceived, 0).expect("Error in sending a message"); - debug!("HANDLE: MessageStatus::Waiting"); - session.message_status = MessageStatus::Waiting; + debug!("HANDLE: SessionStatus::Waiting"); + session.session_status = SessionStatus::Waiting; } - } } debug!("HANDLE: END"); } ``` -Upon receiving the `Action::CheckReply` message, the handler will review the session's status. If the message originated from the program itself and the status is `Status::Sent`, a notification will be sent to the sender to report the absence of a response message. \ No newline at end of file +Upon receiving the `Action::CheckReply` message, the handler will review the session's status. If the message originated from the program itself and the status is `Status::Sent`, a notification will be sent to the sender to report the absence of a response message. + +Moving to the `handle_reply()` function: + +```rust +#[no_mangle] +extern "C" fn handle_reply() { + debug!("HANDLE_REPLY"); + let reply_to = msg::reply_to().expect("Failed to query reply_to data"); + let session = unsafe { SESSION.as_mut().expect("The session is not initialized") }; + + if reply_to == session.msg_ids.0 && session.session_status == SessionStatus::MessageSent { + let reply_message: Event = msg::load().expect("Unable to decode `Event`"); + debug!("HANDLE_REPLY: SessionStatus::ReplyReceived {:?}", reply_message); + session.session_status = SessionStatus::ReplyReceived(reply_message); + } +} +``` diff --git a/docs/03-intermediate/04-delayed-message/delayed-message.md b/docs/03-intermediate/04-delayed-message/delayed-message.md index 1dee642..2490f5a 100644 --- a/docs/03-intermediate/04-delayed-message/delayed-message.md +++ b/docs/03-intermediate/04-delayed-message/delayed-message.md @@ -9,7 +9,7 @@ Conventional approaches by programs on other blockchains rely on external, centr An external transaction acts as a trigger to activate the program and start its logic. For instance, someone can initiate an auction by sending a message to the auction program. After the auction period expires, the program needs to process the auction's outcome. However, this process awaits until someone sends the correct message to the program, triggering this action. -The Gear Protocol addresses this challenge by introducing delayed messaging functionality. Programs within the Gear-powered networks can execute autonomously over an unlimited number of blocks, provided there is sufficient gas for execution. This advancement eliminates the need for centralized components in dApps, enabling them to operate entirely on-chain. +The Gear Protocol enables this logic to be fully onchain and do not rely on external services by introducing delayed messaging functionality. Programs within the Gear-powered networks can execute autonomously over an unlimited number of blocks, provided there is sufficient gas for execution. This advancement eliminates the need for centralized components in dApps, enabling them to operate entirely on-chain. The `msg::send_delayed` function facilitates sending a message after a specified delay, accepting the following parameters: - `program` - the program (or user) to which the message will be directed; diff --git a/docs/03-intermediate/04-delayed-message/testing-delayed-message.md b/docs/03-intermediate/04-delayed-message/testing-delayed-message.md index 6a8ca0b..9ec1ddd 100644 --- a/docs/03-intermediate/04-delayed-message/testing-delayed-message.md +++ b/docs/03-intermediate/04-delayed-message/testing-delayed-message.md @@ -13,23 +13,23 @@ use gtest::{Log, Program, System}; use delayed_message_io::{Action, MessageAction, Event}; const USER: u64 = 3; -const SECOND_PROGRAM_ADDRESS: u64 = 2; +const TARGET_PROGRAM_ADDRESS: u64 = 2; #[test] fn test() { let system = System::new(); system.init_logger(); - let first_program = Program::current(&system); - let second_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/second_program.opt.wasm"); + let proxy_program = Program::current(&system); + let target_program = Program::from_file(&system, "target/wasm32-unknown-unknown/debug/target_program.opt.wasm"); - let result = second_program.send_bytes(USER, []); + let result = target_program.send_bytes(USER, []); assert!(!result.main_failed()); - let second_program_address: ActorId = SECOND_PROGRAM_ADDRESS.into(); - let result = first_program.send(USER, second_program_address); + let target_program_address: ActorId = TARGET_PROGRAM_ADDRESS.into(); + let result = proxy_program.send(USER, target_program_address); assert!(!result.main_failed()); - let result = first_program.send(USER, Action::SendMessage(MessageAction::MakeRandomNumber{range: 1})); + let result = proxy_program.send(USER, Action::SendMessage(MessageAction::MakeRandomNumber{range: 1})); assert!(!result.main_failed()); let log = Log::builder() @@ -39,7 +39,7 @@ fn test() { assert!(result.contains(&log)); - let result = first_program.send(USER, Action::SendMessage(MessageAction::MakeRandomNumber{range: 1})); + let result = proxy_program.send(USER, Action::SendMessage(MessageAction::MakeRandomNumber{range: 1})); assert!(!result.main_failed()); let log = Log::builder() @@ -67,8 +67,8 @@ The following debug messages will appear in the console: [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message ID: MessageId([15, 200, 69, 247, 219, 197, 228, 169, 112, 34, 221, 58, 40, 159, 140, 193, 139, 19, 23, 77, 44, 107, 107, 94, 184, 209, 74, 155, 13, 80, 206, 217]) [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: Message payload: SendMessage(MakeRandomNumber { range: 1 }) -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: Action::SendMessage and MessageStatus::Waiting -[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: MessageStatus::Sent +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: Action::SendMessage and SessionStatus::Waiting +[DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: SessionStatus::MessageSent [DEBUG test] [handle(0x0fc8..ced9)] 0x0100..0000: HANDLE: END [DEBUG test] [handle(0xd560..12fb)] 0x0100..0000: !!!! HANDLE !!!! [DEBUG test] [handle(0xd560..12fb)] 0x0100..0000: Message ID: MessageId([213, 96, 108, 153, 11, 175, 246, 203, 166, 249, 165, 69, 253, 140, 44, 138, 82, 194, 230, 50, 196, 117, 66, 218, 223, 197, 172, 150, 125, 82, 18, 251]) @@ -80,8 +80,8 @@ The following debug messages will appear in the console: [DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: Message payload: CheckReply [DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: HANDLE: Action::CheckReply [DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: HANDLE: No response was received -[DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: HANDLE: MessageStatus::Waiting +[DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: HANDLE: SessionStatus::Waiting [DEBUG test] [handle(0x058e..8c20)] 0x0100..0000: HANDLE: END ``` -Observe that the message identifier has changed; this differs from the behavior observed with `wake()`/`wait()`. \ No newline at end of file +Observe that the message identifier has changed; this differs from the behavior observed with `wake()`/`wait()`. diff --git a/docs/03-intermediate/05-homework/assignment.md b/docs/03-intermediate/05-homework/assignment.md index dfeea0f..bc0dcde 100644 --- a/docs/03-intermediate/05-homework/assignment.md +++ b/docs/03-intermediate/05-homework/assignment.md @@ -17,33 +17,33 @@ The game challenges players to guess the word with the fewest attempts, under ti ## Project Structure -To develop a dynamic and interactive word-guessing game, divide the game process into two distinct programs. The first program will handle the core functionalities, such as selecting a random word from a list and evaluating guesses. The second program will manage user interactions, keep track of the game state, and enforce time constraints. This division aims to create a modular, flexible system that enhances the gaming experience. +To develop a dynamic and interactive word-guessing game, divide the game process into two distinct programs. The Wordle program will handle the core functionalities, such as selecting a random word from a list and evaluating guesses. The Game Session program will manage user interactions, keep track of the game state, and enforce time constraints. This division aims to create a modular, flexible system that enhances the gaming experience. -1. **Description of the First Program**: +1. **Description of the Wordle program**: - Contains "start the game" and "check the word" functions. - A word bank exists within the program for selecting a random word at the game's start. - "Start the game" function initiates the game and selects a random word. - "Check the word" function assesses the player's guess against the hidden word, providing feedback on correct letter positions. -2. **Description of the Second Program**: - - Manages interactions with the first program and oversees the gameplay. +2. **Description of the Game Session program**: + - Manages interactions with the Wordle program and oversees the gameplay. - Tracks previous responses and the number of attempts. - Monitors the elapsed time since the game started to manage time constraints and events. 3. **Interaction Between the Programs**: - - The user initiates the game by sending a message to the second program. - - The second program invokes the first program's "start the game" function. - - The user submitts their guesses to the second program, which forwards them to the first program's "check the word" function. - - The first program returns feedback on the guess's accuracy and letter positions. - - The second program analyzes the result, tracking attempts and time. + - The user initiates the game by sending a message to the Game Session program. + - The Game Session program invokes the Wordle program's "start the game" function. + - The user submitts their guesses to the Game Session program, which forwards them to the Wordle program's "check the word" function. + - The Wordle program returns feedback on the guess's accuracy and letter positions. + - The Game Session program analyzes the result, tracking attempts and time. 4. **Key Implementation Aspects**: - - The second program requires mechanisms to store data about previous moves and track time. - - Efficient interaction with the first program through data exchange and response handling is crucial. + - The Game Session program requires mechanisms to store data about previous moves and track time. + - Efficient interaction with the Wordle program through data exchange and response handling is crucial. -## First Program +## Wordle program -The first program is already implemented. Its functionality includes: +The Wordle program is already implemented. Its functionality includes: Metadata contains: @@ -65,7 +65,7 @@ Functions: - `StartGame` - starts the game, selects a random word and returns the reply as `GameStarted{user: ActorId}`. - `CheckWord` - checks the word and returns the reply as `WordChecked { user: ActorId, correct_positions: Vec,contained_in_word: Vec }`, where in the `correct_positions` returns the indices of letters that are in their place, and `contained_in_word` returns the indices of letters that are contained in the word but are in the wrong place. -The complete code of the first program looks as follows: +The complete code of the Wordle program looks as follows: ```rust pub enum Action { @@ -147,9 +147,7 @@ extern "C" fn handle() { user, correct_positions: matched_indices, contained_in_word: key_indices, - } - } }; @@ -184,10 +182,10 @@ Through these examples, one can see how the program evaluates user guesses and p ## The Homework Assignment -Create a second program that interfaces between the user and the first program. +Create a Game Session program that interfaces between the user and the Wordle program. 1. **Initialization Function (`init()`):** - - Receives and stores the first program's address. + - Receives and stores the Wordle program's address. 2. **Handle Function (`handle()`):** - Manages actions: `StartGame`, `CheckWord`, `CheckGameStatus`. @@ -195,7 +193,7 @@ Let's examine the functionality of each action: - `StartGame`: - The program checks if a game already exists for the user; - - It sends a "StartGame" message to the first program; + - It sends a "StartGame" message to the Wordle program; - Utilizes the `exec::wait()` or `exec::wait_for()` function to await a response; - Sends a delayed message with action `CheckGameStatus` to monitor the game's progress (its logic will be described below); - A reply is sent to notify the user that the game has beeen successfully started. @@ -203,7 +201,7 @@ Let's examine the functionality of each action: - `CheckWord`: - Ensures that a game exists and is in the correct status; - Validates that the submitted word length is five and is in lowercase; - - Sends a "CheckWord" message to the first program; + - Sends a "CheckWord" message to the Wordle program; - Utilizes the `exec::wait()` or `exec::wait_for()` function to await a reply; - Sends a reply to notify the user that the move was successful. @@ -213,6 +211,9 @@ Let's examine the functionality of each action: Specify a delay equal to 200 blocks (10 minutes) for the delayed message. 3. **Handle Reply Function (`handle_reply()`)**: + +Processes reply messages and updates the game status based on responses from the Wordle program. + - Receives reply messages. - Utilizes `msg::reply_to()` to determine the message identifier, i.e., which message was replied to. - Processes and stores the result depending on the reply: @@ -222,10 +223,6 @@ Let's examine the functionality of each action: - If all attempts are used up and the word is not guessed, it switches the game status to `GameOver(Lose)`. - Calls `wake()` with the identifier of the received message to acknowledge the response. - -3. **Handle Reply Function (`handle_reply()`):** - - Processes reply messages and updates the game status based on responses from the first program. - 4. **State Function (`state()`)**: - It is necessary to implement the state() function in order to get all the information about the game.