Author: Li Dagou (Li Aohua)/Post editor: Zhang Handong


All source code of this series:

Github.com/leeduckgo/R…

New Year’s resolutions

Planning to learn a new programming language in 2021, Rust is a good target because it is extremely practical. On the other hand, it allows us to understand computers at a higher level.

This series will be a complete record of my Rust learning process from Rust primary school onwards.

Without further ado, let’s turn it round.

Because it’s a new language (as opposed to Java), the traditional approach of going online to find a good introductory textbook doesn’t work.

So let’s see what fun things Kangkang Rust can do and what fun Repo there is.

Substrate (Polkadot public chain), Libra (Facebook chain), and WeDPR (FISCO BCOS Privacy protection component) are all written in Rust, but upon evaluation, these REPOs are too difficult to be used as a language primer.

It turns out Rust is doing pretty well with WebAssembly:

WebAssembly is a new way of coding that can run in modern Web browsers – it is a low-level assembler like language with a compact binary format that runs close to native performance and provides a compilation target for languages such as C/C ++ so that they can run on the Web. It is also designed to coexist with JavaScript, allowing the two to work together.

In a nutshell

WebAssembly is huge for Web platforms — it provides a way for code written in a variety of languages to run on the Web at near-native speeds. In this case, client software that could not run this way before will be able to run on the Web.

So Rust’s learning path was set, starting with WASM!

Retrieve instance

Now that you have identified the target, you can begin retrieving the corresponding instance. This instance has two conditions:

  • Light article is not, must support the corresponding source
  • The source code must be concise enough to get started

After some searching, I found this:

Project Code:

Github.com/RodionChach…

Operating address:

Rodionchachura. Making. IO/rust – js – sna…

Tutorial Address:

Geekrodion.com/blog/rustsn…

Git Clone

But it didn’t feel like what I wanted because there was so much front-end code.

Then open the official tutorial:

Developer.mozilla.org/zh-CN/docs/…

See:

Rust and WebAssembly have two main use cases:

  • Build a complete Application – The entire Web application is built on Rust!
  • Build application components – Use Rust in an existing JavaScript front end.

For now, the Rust team is focusing on the second use case, so we’ll focus on that as well. For the first use case, see projects like Yew.

Yep, feels like I need Yew!

Yew’s journey of discovery

Find yew’s website first:

Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly.

Github.com/yewstack/ye…

Find the official example:

Yew. Rs/docs/useful – CN /…

As a result, an error was reported…

cargo-web is not compatible with web-sys.
Copy the code

When you encounter a problem, the first time, of course, is to search the official Repo, and then find such an Issue:

Github.com/yewstack/ye…

It is recommended to use trunk

Trunk’s journey of discovery

Jump to Trunk Repo:

Github.com/thedodd/tru…

Clone examples:

Execution ok, good!

But what if you only have a simple example and you can’t learn from it?

Let’s go back to Yew’s Repo and see if there are any instances.

Github.com/yewstack/ye…

There are many Examples, and all of them can run.

Base64 Encoder!

When learning a new computer technology, don’t start from zero to one! Because the difficulty of going from 0 to 1 is too high for beginners. Start by demonizing an existing project.

I chose todomvc, the original looks like this:

The purpose is to change it to a base64-encoder:

Ok, let’s look at the original code:

.fn view(&self) -> Html {
        let hidden_class = if self.state.entries.is_empty() {
            "hidden"
        } else {
            ""
        };
        html! {
            <div class="todomvc-wrapper">
                <section class="todoapp">
                    <header class="header">
                        <h1>{ "todos" }</h1>
                        { self.view_input() } </header> <section class=classes! ("main", hidden_class)>
                        <input
                            type="checkbox" class="toggle-all" id="toggle-all" checked=self.state.is_all_completed() onclick=self.link.callback(|_| Msg::ToggleAll) /> toggle-all" /> todo-list"> { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) }    footer", hidden_class)> todo-count"> { self.state.total() } { " item(s) left" }  filters"> { for Filter::iter().map(|flt| self.view_filter(flt)) }  clear-completed" onclick=self.link.callback(|_| Msg::ClearCompleted)> { format! ("Clear completed ({})", self.state.total_completed()) }    info"> 

{ "

Double-click to edit a todo" }

{ "

Written by " }<a href="https://github.com/DenisKolodin/" target="_blank">{ "Denis Kolodin" }</a></p> <p>{ "Part of " }<a href="http://todomvc.com/" target="_blank"> {"TodoMVC" }</a></p> </footer> </div> } } } ...... Copy the code

Ok, that’s the front end, let’s cut it out:

    fn view(&self) -> Html {
        let hidden_class = if self.state.entries.is_empty() {
            "hidden"
        } else {
            ""
        };
        html! {
            <div class="todomvc-wrapper">
                <h1>{ "encode/decode" }</h1>
                { self.view_input() } <section class=classes! ("main", hidden_class)>
                    <ul class="todo-list">
                        { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) }
                    </ul>
                </section>
            </div>
        }
    }
Copy the code

We can see that the input logic is in view_input(), so we find the function:

fn view_input(&self) -> Html {
        html! {
            // You can use standard Rust comments. One line:
            // <li></li>
            <input
                class="new-todo"
          			/ / change replaceholder
                placeholder="What needs to be encode/decode?"
                value=&self.state.value
                oninput=self.link.callback(|e: InputData| Msg::Update(e.value))
                onkeypress=self.link.batch_callback(|e: KeyboardEvent| {
                    if e.key() == "Enter" { Some(Msg::Add) } else { None }
                })
            />
            /* Or multiline: 
      
*/
}}Copy the code

Msg::Add:

fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Add => {
                //info! ("add things");
                let description = self.state.value.trim();
                let description_handled = format!("{}: {}", description, encode(description.to_string()));

                if! description.is_empty() {let entry = Entry {
                        description: description_handled,
                        completed: false,
                        editing: false};//info! ("{}", entry.description);
                    self.state.entries.push(entry);
                }
                self.state.value = "".to_string(); }...Copy the code

At this point, I want to debug first, so I need to print out some data.

Print = print = print

println!("Input: {}", val);
Copy the code

However, in the Trunk serve command, println! This function is invalid!

A search in trunk and Yew’s Repo failed to find a solution.

But then I noticed that Yew had a Discord Chatroom, so I went to search for the chat logs.

Yummy, as mentioned here just use wASM-Logger.

Crates. IO/crates/wasm…

Add wASM-Logger to your project:

.// in the first of main.rs
#[macro_use] extern cratelog; .fn main() {
		// init wasm logger!
    wasm_logger::init(wasm_logger::Config::default());
    yew::start_app::<Model>();
}
Copy the code

Try calling:

fn update(&mut self, msg: Self::Message) -> ShouldRender {
        matchmsg { Msg::Add => { info! ("add things"); .Copy the code

Completed!

Next find the library in Rust Base64 and call it (where modifications are marked with new) :

.usebase64::{encode, decode}; .fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Add => {
                // newinfo! ("add things");
                let description = self.state.value.trim();
                // new
                let description_handled = format!("{}: {}", description, encode(description.to_string()));

                if! description.is_empty() {let entry = Entry {
                      	// new
                        description: description_handled,
                        completed: false,
                        editing: false};// newinfo! ("{}", entry.description);
                    self.state.entries.push(entry);
                }
                self.state.value = "".to_string();
            }
Copy the code

Run.

Okay, base64-encoder is ready!

Effect:

Cargo. Toml ended up looking like this:

[package]
name = "encoder"
version = "0.1.0 from"
authors = ["Denis Kolodin <[email protected]>"]
edition = "2018"

[dependencies]
strum = "0.20"
strum_macros = "0.20"
serde = "1"
serde_derive = "1"
yew = { path = "./packages/yew" }
yew-services = { path = "./packages/yew-services" }

log = "0.4.6"
wasm-logger = "0.2.0"
base64 = "0.13.0"
Copy the code

The public and private keys and address of ETH are generated

All source code of this series:

Github.com/leeduckgo/R…

This is the second installment of Rust’s learning Notes. In the first post, we created an Encoder, and now we’re going to continue on our path to a +1 difficulty Repo:

Rust library for generating cryptocurrency wallets

Github.com/AleoHQ/wagy…

Changed target 0x1:

Extract the Repo ethereum private key, public key, address generated part, print to the console.

But before the magic change, the author first of all to the last article a little supplement, summarize the knowledge points involved in the last article.

The knowledge points covered in the previous article

  • Assignment of a variable
  • format! Function (connection string)
  • Add and use library, take wASM-Logger as an example
  • Trunk combines with Yew to wasm Rust programs and make them accessible in browsers

Run the wagyu again

The first thing to do is verify that the library meets our needs, so follow the Readme in the Repo and run through it as source code.

# Download the source code
git clone https://github.com/AleoHQ/wagyu
cd wagyu

# Build in release mode
$ cargo build --release
./target/release/wagyu
Copy the code

Success:

In the process, we learned more about cargo:

$ cargo run # direct execution
$ cargo build The executable file is in./target/debug
$ cargo build --release The executable file is under./target/release
Copy the code

Study the Wagyu code

First glance at the directory structure:

.Bass Exercises ── AUTHORS Bass Exercises ── Cargo.Lock Bass Exercises ── Cargo.Toml Bass Exercises ─ License-Apache Bass Exercises ─ License-mit Bass Exercises ── Bitcoin Bass Exercises ─ Ethereum ├ ─ ─ model ├ ─ ─ monero ├ ─ ─ target ├ ─ ─ zcash └ ─ ─ wagyu ├ ─ ─ cli │ ├ ─ ─ bitcoins. Rs │ ├ ─ ─ ethereum. Rs │ ├ ─ ─ mod. Rs │ ├ ─ ─ Monero. Rs │ ├ ─ ─ the parameters │ └ ─ ─ zcash. Rs ├ ─ ─ lib. Rs └ ─ ─ main. RsCopy the code

As we can see, the main entrance is Wagyu.

In Wagyu’s main.rs, submodules in the CLI directory are called, and then submodules at the level of the CLI are called.

The code is as follows:

fn main() - >Result<(), CLIError> {
    let arguments = App::new("wagyu")
        .version("v0.6.3")
        .about("Generate a wallet for Bitcoin, Ethereum, Monero, and Zcash")
        .author("Aleo <[email protected]>")
        .settings(&[
            AppSettings::ColoredHelp,
            AppSettings::DisableHelpSubcommand,
            AppSettings::DisableVersion,
            AppSettings::SubcommandRequiredElseHelp,
        ])
        .subcommands(vec![
            BitcoinCLI::new(),
            EthereumCLI::new(),
            MoneroCLI::new(),
            ZcashCLI::new(),
        ])
        .set_term_width(0)
        .get_matches();

    match arguments.subcommand() {
        ("bitcoin".Some(arguments)) => BitcoinCLI::print(BitcoinCLI::parse(arguments)?) , ("ethereum".Some(arguments)) => EthereumCLI::print(EthereumCLI::parse(arguments)?) , ("monero".Some(arguments)) => MoneroCLI::print(MoneroCLI::parse(arguments)?) , ("zcash".Some(arguments)) => ZcashCLI::print(ZcashCLI::parse(arguments)?) , _ = >unreachable!(),}}Copy the code

If you go to the Ethereum. rs directory again, there is a simple function:

    pub fn new<R: Rng>(rng: &mut R) -> Result<Self, CLIError> {
        letprivate_key = EthereumPrivateKey::new(rng)? ;let public_key = private_key.to_public_key();
        letaddress = public_key.to_address(&EthereumFormat::Standard)? ;Ok(Self {
            private_key: Some(private_key.to_string()),
            public_key: Some(public_key.to_string()),
            address: Some(address.to_string()),
            ..Default::default()
        })
    }
Copy the code

That’s good. I’ll take it!

Copy the necessary files to the new project

  1. New project
$ cargo new hello-crypto-rust
Copy the code

Or make a copy of the previous project.

  1. thewagyutheCargo.tomlCopy the necessary content in
[dependencies]
log = "0.4"
pretty_env_logger = "0.3"

wagyu-ethereum = { path = "./ethereum", version = "0.6.3" }
wagyu-model = { path = "./model", version = "0.6.3" }

arrayvec = { version = "0.5.1" }
base58 = { version = "0.1" }
clap = { version = "~ 2.33.1" }
colored = { version = "1.9" }
digest = { version = "0.9.0" }
either = { version = "1.5.3" }
failure = { version = "0.1.8" }
hex = { version = "0.4.2" }
lazy_static = { version = "1.4.0" }
rand = { version = "0.7" }
rand_core = { version = "0.5.1" }
safemem = { version = "0.3.3" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
tiny-keccak = { version = "1.4" }

[profile.release]
opt-level = 3
lto = "thin"
incremental = true

[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = "thin"
incremental = true
debug-assertions = false

[profile.dev]
opt-level = 0

[profile.test]
opt-level = 3
incremental = true
debug-assertions = true
debug = true
Copy the code
  1. theethereumwithmodelCopy two folders tohello-crypto-rustdirectory

The file directory looks like this:

.├ ── Cargo. Lock ├── Cargo. Toml ├── Ethereum ├─ SRC ├─ targetCopy the code

Add the code

  1. supplementlib.rsfile

Create a new lib.rs file in the SRC directory with the following contents:

pub extern crate wagyu_ethereum as ethereum;
pub extern crate wagyu_model as model;
extern crate pretty_env_logger;
Copy the code

Crate loads the external crate.

Wiki.jikexueyuan.com/project/rus…

  1. writemain.rsFile.

First reference the necessary external modules:

use rand::{rngs::StdRng};
use rand_core::SeedableRng;
use hello_crypto_rust::ethereum::{EthereumPrivateKey, EthereumFormat};
use hello_crypto_rust::model::{PrivateKey, PrivateKeyError, AddressError, PublicKeyError, PublicKey};

#[macro_use] extern crate log;
Copy the code

Then we write the main function:

fn main(){
    pretty_env_logger::init();  // Initialize the pretty_env_logger module
    new(); // Call new
}
Copy the code

Write the new() function:

pub fn new() - >Result<EthereumPrivateKey, CreateError> {
    let rng = &mut StdRng::from_entropy();
    letprivate_key = EthereumPrivateKey::new(rng)? ; info! ("priv: {}", private_key.to_string());
    letpublic_key = private_key.to_public_key(); info! ("pub: {}", public_key.to_string());
    letaddress = public_key.to_address(&EthereumFormat::Standard)? ; info! ("addr: {}", address.to_string());
    Ok(private_key)
}
Copy the code

So we’re using this relative to println! More advanced output, through log output.

Here’s the key grammatical sugar –? For error handling.

Connecting result to match looks ugly; Luckily,? Operators can make this logic nice and clean. ? The operator is used after an expression that returns a Result value and is equivalent to a matching expression where the Err(Err) branch expands into a pre-returned return Err(Err) and the Ok(Ok) branch expands into an Ok expression.

— rustwiki.org/zh-CN/rust-…

Two equivalent functions, one using? None of them:

fn not_use_question_mark() {
    let a = 10;                                                   // If you change this to 9, you will get an error.
    let half = halves_if_even(a);
    let half = match half {
        Ok(item) => item,
        Err(e) => panic!(e),
    };
    assert_eq!(half, 5);
}


fn use_question_mark<'a> () - >Result<i32, &'a str> {              // We must return Result
    let a = 10;
    lethalf = halves_if_even(a)? ;/ / for? The function must return Result
    assert_eq!(half, 5);
    Ok(half)                                                                   
}
Copy the code

Next, we define the enumerated type CreateError, which includes AddressError, PrivateKeyError, and PublicKeyError.

pub enum CreateError {
    AddressError(AddressError),
    PrivateKeyError(PrivateKeyError),
    PublicKeyError(PublicKeyError)
}

impl From<AddressError> for CreateError {
    fn from(error: AddressError) -> Self {
        CreateError::AddressError(error)
    }
}

impl From<PrivateKeyError> for CreateError {
    fn from(error: PrivateKeyError) -> Self {
        CreateError::PrivateKeyError(error)
    }
}

impl From<PublicKeyError> for CreateError {
    fn from(error: PublicKeyError) -> Self {
        CreateError::PublicKeyError(error)
    }
}
Copy the code

Try It!

Achieve success:

The knowledge points covered in this article

  • More uses of cargo
  • lib.rsThe use of the
  • Function and function return value
  • pretty_env_loggerThe use of the
  • Enumeration type toCreateErrorAs an example

About the author:

Li Dagou (Li Haohua), deputy Director of Blockchain Technology and Application Research Center of Shanghai University of International Business and Economics, CTO of Bochian Education, Blockchain certification lecturer of FISCO BCOS (WeBank Blockchain Framework), 5-year blockchain engineer, master of Peking University. Research areas include: blockchain systems, consensus mechanisms, smart contracts, blockchain applications, digital identities, etc.

Content: February issue of Rust Chinese (Rust_Magazine)