エンジニアのためのWeb3開発入門
Web3 とは
ブロックチェーン技術を活用した非中央集権的な web
Web2.0 時代の web はアプリを運営する企業(団体)が一括で情報を管理していたが、Web3 では各個人が自身の情報を管理し、個人同士が相互接続したネットワーク上でアプリが構築される。
Web2.0 時代の中央集権的なサービスでは運営の匙加減でサービス自体が大きく変わったり、運営による情報の操作(垢BANなど)が行えてしまう。
Web3 の非中央集権なサービスでは絶対的な運営が存在しないためそういった改変が行えない。
暗号資産や NFT は「ブロックチェーン上に記録されている取引の履歴」という感じで表現できそう。
履歴を紐解くことで、誰が今いくらの暗号資産をもっているのか、ある NFT の所有者は誰なのか、がわかる。
なので、暗号資産や NFT は「所有権」というワードで抽象化して表現できそう。
この所有権を扱うために暗号通貨取引所や NFT マーケットプレイスといった Web2.0 世代のサービスを利用することもある。
DApp
分散型ネットワーク上に構築されたアプリケーション。
Web2.0 時代はビジネスロジックやデータはすべてサービス自身が一括で管理されていたが、Web3 ではそれらの管理場所やフォーマットが異なる。
データ
ブロックチェーンや分散型ストレージ上にデータが保存される。
これらの情報は全て公開情報となり誰でも参照可能。
データのうちユーザー固有の秘密鍵などの機密情報はユーザー自身がクライアント側でウォレットとして保管する。
ビジネスロジック
ユーザーからの入力を処理しデータを登録するビジネスロジックはブロックチェーン上で実行可能なプログラムのスマートコントラクトが行う。
Web3 とは、ブロックチェーンとは
ブロックチェーンとは P2P ネットワーク上で台帳データ( = トランサクション)を管理するための技術。
ブロックチェーン技術を利用したプロダクトとしてビットコインやイーサリアムがある。
イーサリアムはスマートコントラクトというブロックチェーン上に展開されるプログラム可能な契約ロジックを実装できる。
スマートコントラクトを利用して作成されたアプリケーションが DApps や NFT。
トークン
ブロックチェーン上で発行されるデジタル資産。
ビットコインやイーサリアムなどの通貨としての役割を持つブロックチェーンにおいてはトークン = 暗号資産
通過取引を直接行わないプロダクトにおけるトークン
サプライチェーンなどの直接暗号資産を扱わないプロダクトにおけるトークンの役割はブロックチェーン上の情報の信頼性の担保、情報の質の向上。
トークンを導入することでブロックチェーンで管理されたサプライチェーンに情報を提供することのインセンティブがつけられるので、参加者が増えたり正確な情報の提供に繋がったりして、結果的にサプライチェーン情報の信頼性、詳細度が向上につながる。
スマートコントラクト
取引時にブロックチェーン上で自動的に実行されるプログラム。
スマートコントラクトのコードもトランザクションと同様にブロックチェーン上に記録される。
それゆえ、一度ブロックチェーン上にデプロイしたスマートコントラクトはトランザクションと同様に取り消し、変更ができない。
もしデプロイしたコードにバグがあったりした場合は新しいバージョンのスマートコントラクトを再度デプロイすることになるが、古いバージョンのスマートコントラクトもブロックチェーン上に残り続けるのでバグを完全に無かったものにすることはできない。
ウォレット
暗号資産を管理するためのツール。
ウォレットはブロックチェーン上では管理されず、ユーザー自身がデバイス上などで管理する。
ウォレットにはトランザクションの署名を行う秘密鍵、公開鍵の他に暗号資産の残高やトランザクション履歴などが含まれる。
ストレージ
ブロックチェーンにデータを保存する際、トランザクションをリクエストしたユーザーやスマートコントラクトをデプロイした開発者はそのトランザクションを処理したノードに対してガス代という形で手数料を支払う必要がある。
ガス代は処理の負荷やブロックチェーンに保存するデータ容量に応じて高額になるため、画像や動画などの容量の大きいデータをブロックチェーンに保存してしまうと高額なガス代が発生してしまう。
そのため、そういった大容量ファイルを扱う場合は IPFS や Swarm といった分散型ストレージにデータを格納し、ブロックチェーンにはそのデータのアドレスを保存するのが一般的。
イーサリアム
アカウント
ブロックチェーン技術におけるアカウントは、デジタル資産を管理、識別するためのアドレスや識別子のことを言う。
EOA (Externally Owned Account)
ユーザーが直接保有するアカウントで対となる秘密鍵を持つ。
通常は MetaMask などのウォレットに格納される。
この秘密鍵でトランザクションに署名して送信することで、トランザクションを開始することができる。
暗号資産の送金先のアドレスは秘密鍵と対の公開鍵から生成される。
CA (Contract Account)
スマートコントラクトをデプロイすることで付与されるアカウントで、秘密鍵は持たずにスマートコントラクトのソースコードを持つ。
EOA は CA のアドレスに対してトランザクションを送信でき、それを受け取ったスマートコントラクトはソースコードに記載された処理を実行する。
CA は storage と呼ばれる状態を管理する領域を持ち、過去のトランザクションの結果やコントラクトの内部状態を保存することができる。
EVM(Ethereum Virtual Machine)
スマートコントラクトを実行する仮想環境。
スマートコントラクトはブロックチェーン上の全てのノードで実行され、それらの結果が同じであることでシステム全体の情報の信頼性を担保している。
そのためスマートコントラクトの処理は同じ入力、同じ状態であれば常に同じ結果を返す決定論的アルゴリズムが採用され、EVM もそれを前提としている。
State
ブロックチェーン全体の状態を管理するデータ構造。
State には全てのアカウントの暗号資産残高や全てのスマートコントラクトのコードや storage が含まれる。
State はビットコインのハッシュのように各ブロックは State Root というハッシュ値を持ち、これが一致することでブロックチェーン上の全てのノードが同じ状態を持つことを保証している
オラクル
ブロックチェーン内部のデータしか扱えないスマートコントラクトがブロックチェーン外部の情報(外部 API やデータベース)を取得するための仕組み。
スマートコントラクトが外部情報を利用する際はオラクルにデータの収集をリクエストし、オラクルは外部データソースからリクエストされたデータを収集しスマートコントラクトに返す。
スマートコントラクトはオラクルから渡された値で処理を実行する。
ガス代
スマートコントラクトを実行するための手数料。
イーサリアムではスマートコントラクトの処理はブロックチェーンの参加者が提供している CPU で行われるため、スマートコントラクト実行者へのインセンティブとしてガス代が設定されている。
ガス代は トランザクション実行に必要なガス(処理量、データ量) * ガス単価
で決まる。
Proof of Stake
保有する暗号資産の量によってブロックを追加する権限を与えるコンセンサスアルゴリズム。
ビットコインや従来のイーサリアムが採用していた PoW では難しい計算を最初にクリアしたマイナーがブロックを追加し報酬を得られるのに対し、PoS ではバリデーターがブロックの作成と検証を担当する。
イーサリアムにおけるバリデーターにはネットワークに 32ETH をデポジットすることで誰でもなることができるが、不正を働くとデポジットが没収されてしまう。
過半数の通貨をもつことで不正が行えるようになるが現実的ではないので安全性も担保される。
シャーディング
ブロックチェーンをビーコンチェーンとシャードチェーンに分割することでトランザクションを並行して処理できるようにする技術。
個別のトランザクションは複数あるシャードチェーンのうちの一つで処理が行われ、複数のシャードチェーンに記録されたブロックが最終的にビーコンチェーンにマージされる。
イーサリアムの開発
Solidity
イーサリアムのスマートコントラクトを開発するための言語。
イーサリアムの特性を利用するために設計されており、イーサリアムのネイティブトークンの ETH の送金、作成などの機能を持つ。
また、他のスマートコントラクトを呼び出したり、スマートコントラクトないでイベントを発生させそれを別のスマートコントラクトがリスニングし別の処理を行う pub sub などの機能も持つ。
OpenZeppelin
イーサリアムのスマートコントラクト開発を安全で効率的に行えるためのオープンソースライブラリ。
専門家による事前検証済みのスマートコントラクトテンプレートや、 スマートコントラクト開発におけるセキュリティに関するベストプラクティスをまとめたドキュメントやツールを提供している。
テンプレートには NFT、アクセス制御、DAO などが含まれる。
Hardhat
イーサリアムのスマートコントラクトの開発環境。
イーサリアムの擬似ネットワーク環境を提供しており、実際のブロックチェーンネットワークにデプロイせずにスマートコントラクトの動作確認ができる。
また、スマートコントラクトのコンパイル、デプロイといった複雑な工程を簡単に実行するコマンドも提供している。
イーサリアム開発環境
Hardhat
イーサリアムの開発環境として Hardhat を利用する。
javasctipt からスマートコントラクトへのアクセスをサポートする Hardhat toolbox もインストールする。
yarn add --dev hardhat @nomicfoundation/hardhat-toolbox
hardhat init で Hardhat 開発環境の作成ができる
% yarn hardhat init
yarn run v1.22.19
...
👷 Welcome to Hardhat v2.22.19 👷
✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · /path/to/project
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Help us improve Hardhat with anonymous crash reports & basic usage data? (Y/n) · y
tree -I node_modules
.
├── README.md
├── contracts
│ └── Lock.sol
├── hardhat.config.ts
├── ignition
│ └── modules
│ └── Lock.ts
├── package.json
├── test
│ └── Lock.ts
├── tsconfig.json
└── yarn.lock
OpenZeppeline
yarn add @openzeppeline/contracts
smart contract
変数
// Some string type variables to identify the token.
string public name = "My Hardhat Token";
string public symbol = "MHT";
// The fixed amount of tokens, stored in an unsigned integer type variable.
uint256 public totalSupply = 1000000;
// An address type variable is used to store ethereum accounts.
address public owner;
// A mapping is a key/value map. Here we store each account's balance.
mapping(address => uint256) balances;
他のプログラム言語と同様に変数の定義が可能。
public がついている変数はコントラクト外部から参照可能な変数で、public をつけて変数を定義すると自動的に getter 関数が生成される。(例: name 変数の値を取得する name() 関数)
関数
/**
* A function to transfer tokens.
*
* The `external` modifier makes a function *only* callable from *outside*
* the contract.
*/
function transfer(address to, uint256 amount) external {
// Check if the transaction sender has enough tokens.
// If `require`'s first argument evaluates to `false`, the
// transaction will revert.
require(balances[msg.sender] >= amount, "Not enough tokens");
// Transfer the amount.
balances[msg.sender] -= amount;
balances[to] += amount;
// Notify off-chain applications of the transfer.
emit Transfer(msg.sender, to, amount);
}
/**
* Read only function to retrieve the token balance of a given account.
*
* The `view` modifier indicates that it doesn't modify the contract's
* state, which allows us to call it without executing a transaction.
*/
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
関数も他の言語と同じように定義でき、返り値なども設定できる。
external
external をつけて宣言した関数はコントラクト外部からの呼び出し専用の関数になる。
public をつけて宣言することもでき、その場合はコントラクトの外部からも内部からも呼び出し可能。
external の方がガス使用効率がいい。
msg
現在の関数呼び出しに関する情報を提供するグローバル変数。
msg.sender には関数を呼び出したアカウントのアドレスが入る。
その他に value や data などのフィールドも持つ。
reqire
関数実行におけるバリデーション。
require の一つ目の引数が false になる場合、2つ目の引数のメッセージと共にトランザクションは取り消される。