Rustスマートコントラクト開発日記(1)
BlockSec
2022-03-29 10:25
本文约3914字,阅读全文需要约16分钟
BlockSec は監査サービスの提供に加えて、セキュリティ開発の観点からコミュニティへのサポートをさらに提供したいと考えています。

1. EVM or WASM?

イーサリアムの人気により、スマート コントラクトについて話すとき、デフォルトで EVM に基づいたスマート コントラクトを開発するために Solidity 言語がよく使用されます。しかし、イーサリアムの遅いブロック時間と高い取引手数料という欠点のため、ますます多くの最適化テクノロジーと新しいパブリックチェーンが立ち上げられています。 WASMは代表的な技術の一つです。新しいバイナリ構文である WASM には、小さい命令サイズ、高速な動作速度、メモリの安全性など、多くの利点があります。したがって、WASM上で実行されるスマートコントラクトは、占有されるブロックチェーンリソースを大幅に削減し、ブロック生成の速度と効率を大幅に向上させ、より安定して実行できるため、ユーザーはより優れたエクスペリエンスを得ることができます。 WASM は、Rust、C、C++、TypeScript、AssemblyScript など、さまざまなフロントエンド開発言語をサポートしています。適応性とツールチェーン、そして言語自体のセキュリティを考慮すると、Rust は非常に良い選択肢の 1 つです。

2. BlockSec の選択

BlockSec の使命は、Defi エコシステム全体をより安全にすることです。したがって、監査サービスの提供に加えて、セキュリティ開発の観点からもコミュニティへのサポートをさらに提供していきたいと考えています。 Rust と WASM の多くの利点に基づいて、このテクノロジー スタックに特化した一連の共有を行うことにしました。今後も注目していただければ幸いです。私たちは現在、より人気のあるパブリック チェーン プロジェクトのいくつかを調査しましたが、NEAR パブリック チェーンも同じテクノロジー スタックを使用しています。 NEAR は WASM コントラクトをネイティブにサポートし、スマート コントラクトを開発するための Rust 言語と AssemblyScript をサポートします。したがって、NEARパブリックチェーンに基づいて共有と議論を開始します。

3. Rust を使用してスマート コントラクトを開発する

Rust 言語は Mozilla によって開発されており、プログラムがコンパイルされると、驚くべき速度で実行され、メモリ使用率が非常に高く、関数型およびオブジェクト指向のプログラミング スタイルをサポートします。おそらく多くの学生はまだ Rust 言語に慣れていないでしょう。しかし心配しないでください。このブログから、BlockSec は皆さんと協力して Rust の霧を取り除き、誰もが Rust を使用して効率的で安全なスマート コントラクトを開発できるようにします。

4. 環境構築

4.1 IDEの使用

新しい言語を使って開発する方法を学ぶときは、優れた IDE を選択する必要があります。ここで、BlockSec は、毎日のニーズをほぼ満たせる Rust プラグイン (Rust-analyzer など) とともに Visual Studio Code を使用することを推奨します。条件が整えば、Jetbrains Clion + Rust プラグインを試すこともできます。学生は無料で使用できます。

4.2 Rust ツールチェーンのインストール

優れた IDE を持っている場合は、当然、Rust をダウンロードしてインストールする必要があります。 Rust は非常にシンプルで便利なインストール方法を提供します。 Linux システムでは、次のコード行を実行するだけで、Rust が自動的にダウンロードされ、インストールされます。

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

インストールが完了したら、$rusup --version を実行して、インストールが成功したかどうかを確認できます。 rusup は Rust ツールチェーンのマネージャーとして機能し、これらのツールチェーンとその関連コンポーネントをインストール、削除、更新、選択、管理するためのメソッドを提供します。次に、次のコマンドを実行して、WASM (WebAssembly) ターゲットをツールチェーンに追加する必要があります。

$ rustup target add wasm32-unknown-unknown

5. 最初のRustコントラクト

ようやく本題に到達しました。ここでは、各スマート コントラクト プロジェクトを深く分析することで、Rust を使用してスマート コントラクトを作成する方法を理解し、習得できるようにします。 Rust 言語自体に興味がある場合は、インターネット上に多くのチュートリアルがあるので、それを参照することもできます。

5.1 Rustのパッケージマネージャー

Rust のオープンソース コミュニティ全体のサポートにより、さまざまなサードパーティ ライブラリが無限の流れで出現します。これらのライブラリをより適切に管理するために、Cargo が登場しました。上記のインストール コマンドは、同時に Cargo をインストールするのにも役立ちます。 Cargo は、新しい Rust プロジェクトの作成、Rust プロジェクトが依存するライブラリのダウンロードとコンパイル、プロジェクト全体の完全なビルドなどのタスクで開発者を支援します。

5.2 最初の Rust コントラクト プロジェクトを作成する

開発環境の準備ができたら、まず Cargo を使用して新しいコントラクト プロジェクトを作成し、StatusMessage という名前を付けます。

$ cargo init --lib StatusMessage

プロジェクトのディレクトリツリーは以下のとおりです。

StatusMessage/
├── Cargo.toml
└── src
          └── lib.rs

5.3 契約の宣言

スマート コントラクト (スマート コントラクト) では、多くの場合、一連の契約ステータス データを維持する必要があります。 src/lib.rs に記述された次のコード部分は、StatusMessage と呼ばれる単純なコントラクトを宣言します。

1  #[near_bindgen]
2  #[derive(BorshDeserialize, BorshSerialize)]
3  pub struct StatusMessage {
4      records: LookupMap,
5 }

次に、上記の 5 行のコードを注意深く分析します。 1 行目と 2 行目はコメントと同様に # で始まります。実際、これは Rust のマクロの形式です。行 3 ~ 5 を入力として受け取り、マクロの定義に従って出力を生成します。たとえば、最初の行の #[nearbindgen] は、実際には、near-sdk-macros-version パッケージの Nearbindgen 関数によって定義されており、ここでマクロが自動的に挿入コードを生成するために使用されます (Macros-Auto-Generated Injected Code、参照) MAGICとして)。

理解できなくても大丈夫です。 1 行目と 2 行目の機能を知る必要があるだけです。具体的には、#[nearbindgen] アノテーションが付けられた構造体が NEAR 上のスマート コントラクトになります。他の構造体は通常の構造体です。したがって、[nearbindgen] は NEAR によって開発され、開発者が利用できるパッケージです。 2 行目の #[derive(BorshDeserialize, BorshSerialize)] はシリアル化と逆シリアル化に使用され、コントラクトの状態をチェーン上でバイナリ形式で送信できるようにします。 3 ~ 5 行目は、スマート コントラクトのステータスを維持する StatusMessage と呼ばれる構造体です。そして4行目にステータスの内容が記述されています。この構造体には、 records という名前のメンバー変数が 1 つだけ含まれています。その型は LookupMap であり、単純に辞書型とみなすことができます。キーと値はどちらも通常の文字列型です。

5.4 契約のデフォルトの設定

コントラクトを宣言するとき、多くの場合、そのデフォルト値を定義する必要があります。次のコードは、コントラクト StatusMessage のデフォルト値を設定します。

1  impl Default for StatusMessage {
2      fn default() -> Self {
3          Self {
4              records: LookupMap::new(b"r".to_vec()),
5         }
6     }
7 }

このうち、1行目はStatusMessageのデフォルト値の実装であることを宣言しています。 2 行目では、メソッド名がデフォルトで戻り値が Self であることを宣言しています。 Self は Rust の現在のモジュール スコープを表し、具体的には StatusMessage インスタンスを表します。 3 ~ 5 行目はインスタンスの定義です。インスタンスにはレコードのみが含まれるため、LookupMap 型の変数が含まれます。バイナリ配列 b を渡すことによって"r".tovec(),LookupMap を初期化できます。 LookupMap の新しいメソッドは NEAR 自体によって定義されます。"r".tovec() この LookupMap に格納されているキーのプレフィックスを示します。

5.5 契約方法の定義

構造体を使用してコントラクトの状態を定義した後、これらの公開されたメソッドを外部トランザクションを通じて呼び出せるように、一連のメソッドも定義する必要があります。以下は、現在のコントラクト内のレコード値をそれぞれ変更および取得できる 2 つの定義されたメソッドです。コントラクトのメソッドを定義するときは、1 行目に示すように #[near_bindgen] も追加する必要があることに注意してください。

1  #[near_bindgen]
2  impl StatusMessage {
3      pub fn set_status(&mut self, message: String) {
4          let account_id = env::signer_account_id();
5          self.records.insert(&account_id, &message);
6     }
7
8      pub fn get_status(&self, account_id: String) -> Option   {
9          return self.records.get(&account_id);
10     }
11 }

2 行目の impl キーワードは、StatusMessage を具体的に実装していることを示しています。

行 3 ~ 6 ではメソッド setstatus を定義しています。この関数は、現在のコントラクトの状態を設定するために使用されます。これらの 3 番目では、メソッド名と変数を宣言します。この関数には、&mut self と message: String という 2 つの変数があります。 &mut は self への参照を表し、self の内容を変更する可能性があります。また、message: String は、メッセージの種類が String であることを示します。同時に、関数はキーワード pub で修飾されます。pub fn で修飾された関数のみが外部トランザクションから呼び出すことができ、パブリックであることを示します。

行 4 はローカル変数 accountid を定義しており、その値は env::signeraccountid() を通じて取得され、トランザクション署名を開始したユーザー ID を示します。

行 5 では、accountid をキーとして、message を値としてレコードに挿入します。 message はユーザーによって渡される String 型の変数であることに注意してください。 &message はメッセージへの参照を意味します。

行 8 ~ 10 では、getstatus という名前の別の関数を宣言しています。 setstatus とは異なり、getstatus は None または String 型の値を返します。ここでは Option を使用してそれを表します。

この問題の概要とプレビュー

この問題の概要とプレビュー

これは、Rust コントラクトに関する BlockSec の最初のブログです。この号では、Rust コントラクトの背景と、NEAR チェーンに基づいて単純なコントラクトを作成する方法について説明します。次回は、Rust を使用して、コントラクトをデバッグするために作成したコントラクトの単体テスト ケースを作成する方法についてさらに説明します。

BlockSec
作者文库