Luisa Medova
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 1 lesson: Hello world program Smart Contract is an equivalent of a microservice which is stored on the blockchain network and is the essential building block of a decentralized application. One of the features of the Gear protocol is the `Actor model` for message-passing communication. The principles of `Actor model`: - Actors are modeled as independent computational entities. - The immutable messages are used to communicate between actors. - Actors do not share state and if any information is shared, it’s done via message only. Actors control access to the state and nobody else can access their state. This means there is no shared, mutable state. <center> <img src="https://i.imgur.com/lZaVIyR.png" width=500> </center> Each actor has a queue attached where the incoming messages are enqueued. Messages are picked from the queue and processed by the actor, one at a time. An actor can respond to the received message by sending immutable messages to other actors. It can also create a new set of actors or update its own state. <center> <img src="https://i.imgur.com/CVVytx3.png" width=350> </center> The Actor is a single instantiation of a contract, which can perform several actions. When the message is sent to the contract, one of the entry points is called. A contract contains up to 3 entry points the perform various functions during the program life cycle: - `init()` - is called once per smart contract life time: ``` #[no_mangle] extern "C" fn init() {} ``` - `handle()` - is called every time the program receives an incoming message. ``` #[no_mangle] extern "C" fn handle() {} ``` - `handle_reply()` - is used for processing the reply to the sent message. ``` #[no_mangle] extern "C" fn handle_reply() {} ``` ### 1 part We will create our first smart contract program called "Hello World." Open in gitpod: https://github.com/LouiseMedova/starter. Install the tools required to build smart contract in Rust. Gitpod always comes with the latest available Rust toolchain pre-installed using Rust compiler``rustup``. Let's install a nightly version of the toolchain with``rustup``: ``` rustup toolchain add nightly ``` As we will be compiling our Rust smart contract to Wasm, we will need a Wasm compiler. Let's add it to the toolchain: ``` rustup target add wasm32-unknown-unknown --toolchain nightly ``` Let's declare entry points in `lib.rs` file: ``` #![no_std] use gstd::{prelude::*}; #[no_mangle] extern "C" fn init() {} #[no_mangle] extern "C" fn handle() {} ``` and build the project: ``` cargo build --release ``` Let's import `msg` module from `gstd` library. ``` use gstd::{prelude::*, msg}; ``` The `msg` module allows users to process incoming messages, obtain the necessary information about the sender or the message content, and send replies or new messages to other actors. We'll use the `reply` function that sends a new message as a reply to the message currently being processed: ``` #[no_mangle] extern "C" fn handle() { msg::reply(String::from("Hello"), 0).expect("Error in sending a reply message"); } ``` The first argument is the payload. The second argument is the value to be transferred from the current program account to the reply message target account. Let's write a simple test.We'll create a new directory called `tests` at the top level of our project directory, next to the `src` director. In that directory, we’ll create a file `hello_world_test.rs` where we’ll write tests for our contract. ``` mkdir tests cd tests touch hello_world_test.rs ``` In our test file, we’ll need to import the necessary modules from the gtest library: ``` use gtest::{Log, Program, System}; ``` We’ll also define a test function: ``` #[test] fn hello_test() {} ``` Before testing our smart contract, we need to initialize the environment for running programs. We can do this using the System module from gtest. The system emulates the node behaviour: ``` let sys = System::new(); ``` Next, we need to initialize our program. ``` let hello_program = Program::current(&system); ``` The uploaded program has its own id. You can specify the program id manually using the `current_with_id` constructor. ``` let hello_program = Program::current_with_id(&system, 100); ``` If you don't specify the program id, the id of the first initialized program will be `1`, and the next program initialized without an id specification will have the second id and so on. In the next step, we’ll send messages to our program. ``` hello_program.send(10, String::from("Init message")); ``` The first argument in the `send` is a sender id, the second one is a message payload. The first message to the program in `gtest` is also the init message. Let's go to the `lib.rs` file and add the proccessing of the incoming message: ``` #![no_std] use gstd::{prelude::*, msg, debug}; #[no_mangle] extern "C" fn init() { let init_message: String = msg::load().expect("Can't load the incoming message"); debug!("Program initialized {:?}", init_message); } ``` Let's also add `init_logger()` to initialize printing logs into stdout: ``` #[test] fn hello_world() { let system = System::new(); system.init_logger(); let hello_program = Program::current_with_id(&system, 100); hello_program.send(10, String::from("Init message")); } ``` and run the test: ``` cargo test --release ``` If everything is working correctly, we should see the debug message in our console: ``` running 1 test [DEBUG hello_world] Program initialized "Init message" test hello_world ... ok ``` Sending functions in the gtest library will return RunResult structure. It contains the final result of the processing message and others, which were created during the execution. ``` let res = hello_program.send(10, String::from("Init message")); ``` We check that the result of the proccessing the message was successful: ``` static mut GREETING: Option<String> = None; ``` Let's send the next message to our program. ``` let res = hello_program.send(10, String::from("Handle message")); assert!(!res.main_failed()); ``` This message will be proccessed through the handle function. Let's add `debug` to the `handle` and run the test: ``` #[no_mangle] extern "C" fn handle() { debug!("Handle"); msg::reply(String::from("Hello"), 0).expect("Error in sending a reply message"); } ``` Here we should check that the program replied with `hello` message. ``` let decoded_reply = res.decoded_log::<String>(); println!("{:#?}", decoded_reply); ``` Since the message payload is `String`, we indicate the type `String` in the `decoded_log` function. To see the output from print statements, we run the tests with the nocapture flag: ``` cargo test --release -- --nocapture ``` We can see that we've received a message from the program with `Hello` payload: ``` [ DecodedCoreLog { id: MessageId(0x3c95446e19b23ce659fa6e5cec08e814af11d18cc2ec26378f98e3aa31d6882d), source: ProgramId(0x6400000000000000000000000000000000000000000000000000000000000000), destination: ProgramId(0x0a00000000000000000000000000000000000000000000000000000000000000), payload: "Hello", status_code: Some( 0, ), }, ] ``` Usually in tests we don't need to print the result, but check that the result contains the expected message. To do this, we can use the Log structure from the gtest lib and build the log we are expecting to receive. Specifically, we can use the `Log` structure from the `gtest`lib: ``` use gtest::{System, Program, Log}; #[test] fn hello_world() { ... let expected_reply = Log::builder().dest(10).payload(String::from("Hello")); assert!(res.contains(&expected_reply)); } ``` Run the test and make sure that everything is fine. ### 2 part Let’s add more functionality to our program by introducing two new messages: `SendHelloTo` and `SendHelloReply`. Our program will receive 2 messages: - `SendHelloTo`: having received this message, the program will send `hello` to the specified address; - `SendHelloReply`: the program replies with a `hello` message to the account that sent the current message. As we saw from the previous lesson, we’ll have to decode the message the program received. We’ll define an enum InputMessages that will be used to decode the incoming message. ``` #[derive(Encode, Decode, TypeInfo, Debug)] pub enum InputMessage { SendHelloTo(ActorId), SendHelloReply, } ``` The SendHelloTo variant includes an ActorId field where the program will send the hello message. We also need to add derive macros ``#[derive(Encode, Decode, TypeInfo)]`` to the enum for encoding and decoding in messages. We’ll also define a static mutable variable `GREETING` as an `Option<String>``. ``` static mut GREETING: Option<String> = None; ``` Until the program is initialized, the `GREETING` equals `None`. After the initialization, the `GREETING` will become `Some(String)`. ``` #[no_mangle] extern "C" fn init() { let init_message: String = msg::load().expect("Can't load the incoming message"); debug!("Program initialized {:?}", init_message); unsafe { GREETING = Some(init_message)}; } ``` Next, we’ll decode the incoming message in the handle function and define the message the program received: ``` #[no_mangle] extern "C" fn handle() { let message: InputMessage = msg::load().expect("Can't decode `InputMessage`"); debug!("Incoming message {:?}", message); let greeting = unsafe { GREETING.as_ref().expect("The contract is not initialized")}; match message { InputMessage::SendHelloTo(account) => { msg::send(account, greeting, 0).expect("Can't send a `SendHelloTo` message"); }, InputMessage::SendHelloReply => { msg::reply(greeting, 0).expect("Can't send a `SendHelloReply` message"); } } } ``` Let's test our updated contract: ``` #[test] fn hello_world() { let system = System::new(); system.init_logger(); let hello_program = Program::current_with_id(&system, 100); let greeting = String::from("Hello world!"); let res = hello_program.send(10, greeting.clone()); assert!(!res.main_failed()); let recipient = 200; let res = hello_program.send(10, InputMessage::SendHelloTo(recipient.into())); let expected_log = Log::builder().dest(recipient).payload(greeting.clone()); assert!(res.contains(&expected_log)); let expected_msg = res.decoded_log::<String>(); println!("{:#?}", expected_msg); let res = hello_program.send(10, InputMessage::SendHelloReply); let expected_log = Log::builder().dest(10).payload(greeting.clone()); assert!(res.contains(&expected_log)); let expected_msg = res.decoded_log::<String>(); println!("{:#?}", expected_msg); } ``` ### 3 part Next step is to upload the program to the chain. But before it we should learn the program `metadata`. `Metadata` helps the client side of dApp to understand the smart contract interface. Let's create a crate for metadata: ``` cargo new io --lib ``` The `Cargo.toml` file of the `io` crate: ``` [package] name = "hello-world-io" version = "0.1.0" edition = "2021" [dependencies] gstd = { git = "https://github.com/gear-tech/gear.git", features = ["debug"], rev = "78dfa07"} gmeta = { git = "https://github.com/gear-tech/gear.git", rev = "78dfa07"} scale-info = { version = "2", default-features = false, features = ["derive"] } parity-scale-codec = { version = "3", default-features = false } ``` To describe the metadata interface we use `gmeta` crate: ``` use gmeta::Metadata; ``` We declare the struct for our program `metadata`: ``` pub struct HelloMetadata; ``` and describe its interface. The input for `init` function is a `String`. ``` use gmeta::{InOut, Metadata}; pub struct HelloMetadata; impl Metadata for HelloMetadata { pub type Init = InOut<String, ()>; } ``` The input for the `handle` function is the `InputMessage` enum, and accordingly, the output is `String`. Let's copy the `InputMessage` enum fron the `hello` crate: ``` #[derive(Encode, Decode, TypeInfo, Debug)] pub enum InputMessage { SendHelloTo(ActorId), SendHelloReply, } impl Metadata for HelloMetadata { type Init = InOut<String, ()>; type Handle = InOut<InputMessage, String>; ... } ``` Then we define the program state. It is also `String` (It's a set greeting). ``` impl Metadata for HelloMetadata { type Init = InOut<String, ()>; type Handle = InOut<InputMessage, String>; type State = String; } ``` ```Metadata``` has also `Signal`, `Reply` and `Others` types. We don't need them now but still we have to declare them: ``` impl Metadata for HelloMetadata { type Init = InOut<String, ()>; type Handle = InOut<InputMessage, String>; type State = String; type Reply = (); type Signal = (); type Others = (); } ``` It is possible to read the program state using the state function. Reading State is a free function and does not require gas costs. Let’s define this function in lib.rs file of the hello-world program: ``` #[no_mangle] extern "C" fn state() { let state = unsafe {GREETING.as_ref().expect("The contract is not initialized")}; msg::reply(state, 0).expect("Unable to share the state"); } ``` To make it possible to verify metadata for a program, we’ll use the metahash() function: ``` #[no_mangle] extern "C" fn metahash() { let metahash: [u8; 32] = include!("../.metahash"); msg::reply(metahash, 0).expect("Unable to share the metahash"); } ``` It’s necessary to add the `hello-world-io` crate to `dependencies` and `build-dependencies` in `Cargo.toml` in the `hello-world` program: ``` [package] name = "hello-world" version = "0.1.0" edition = "2021" [dependencies] gstd = { git = "https://github.com/gear-tech/gear.git", features = ["debug"], rev = "78dfa07"} scale-info = { version = "2", default-features = false, features = ["derive"] } parity-scale-codec = { version = "3", default-features = false } hello-world-io = { path = "io"} [build-dependencies] gear-wasm-builder = { git = "https://github.com/gear-tech/gear.git", rev = "78dfa07" } hello-world-io = { path = "io"} [dev-dependencies] gtest = { git = "https://github.com/gear-tech/gear.git", rev = "78dfa07"} ``` We also need to change the `build.rs` file using the following code: ``` use hello_world_io::HelloMetadata; fn main() { gear_wasm_builder::build_with_metadata::<HelloMetadata>(); } ``` After building the program, a `meta.txt` file will be generated as a result of the smart-contract compilation. This metadata file can be used in UI applications that will interact with this smart contract. ### 4 part Let's upload our program to the chain. The easiest way to upload the program is to use the “Upload program” option in the Gear [Idea portal](idea.gear-tech.io). First, you need to create an account and connect to Gear Idea. Follow the [instructions](https://wiki.gear-tech.io/docs/idea/account/create-account) provided at to create your account. Once you've logged in, you can select the network on which you want to upload the program by clicking on the gear icon in the lower left corner of the screen. For the Gear Academy workshop, select the workshop node (`wss://node-workshop.gear.rs:443`) and click the "Switch" button. Select the workshop node and click on the `Switch` button: ![](https://i.imgur.com/Fz65tGe.png) Get the test balance clicking on the button in the top right corner: ![](https://i.imgur.com/ETO37KY.png) Then select `Programs` in the left column and click on the Upload program button: ![](https://i.imgur.com/0TVRADM.png) Choose the file `hello_world.opt.wasm` located in the `target/wasm32-unknown-unknown/release` folder: ![](https://i.imgur.com/trjqyhC.jpg) Then add `meta.txt` file located in the project root: ![](https://i.imgur.com/ZA9EH0w.jpg) Enter the name for your program (for example, `Hello_World`) and set the greeting message: ![](https://i.imgur.com/fRCJdCZ.png) If the program has successfully uploaded, you will see it in the program. ![](https://i.imgur.com/Jz3hOT3.png) Now you can send messages to your program: ![](https://i.imgur.com/3wgV9jg.jpg) You can also read the program state (Click on `Read full state` button): ![](https://i.imgur.com/WFOVAVm.png) It’s our greeting string that was set during the program initialization. ![](https://i.imgur.com/zIPNZvt.png)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully