5分でわかるイーサリアムやsolidityの基礎

ネットワーク IT

CryptoZombieやってsolidityの基礎を勉強してみました

CryptoZombieでSolidityの基礎全6lessonをやってみたので、イーサリアムの基礎的な内容と合わせて私なりに簡単にまとめてみました。

イーサリアムやSolidityのことはまだまったく知らないけど興味あるという方や、CryptoZombieの基礎をひととおりやってみた方の復習として是非参考にしてみてください!

SolidityとはEthereum上で使う開発言語

Ethereum(イーサリアム)とは「分散型アプリケーション(dApps) 」や「スマートコントラクト」を動かすためのブロックチェーンプラットフォームで、Solidityはそのイーサリアム上で動くアプリケーションを作るための開発言語です。ちなみに、日本ではそのプラットフォーム上で使われる暗号通貨もイーサリアムと呼ばれることが多いので、イーサリアムという言葉が出てきた場合は文脈でどちらのことを指しているのかを判断する必要があります。海外では暗号通貨のほうをイーサと呼んで区別されているようです。本記事でも暗号通貨はイーサと呼んで区別します。

「分散型アプリケーション」とは、どこかの特定のサーバで動くアプリケーションではなく、プラットフォームに参加しているコンピュータ(ノード)がお互いにデータを保持し動作が維持されるソフトウェアプログラムです。現状ではGAFAのように特定の企業がプラットフォームを牛耳っていますが、「分散型アプリケーション」ではプラットフォームが特定の企業に依存せずに、文字通り「使用しているみんなのもの」になるのではないかと期待されています。

「スマートコントラクト」とはプラットフォーム上で人の手を介さずに契約内容を自動で実行してくれる仕組みのことです。プログラム上であらかじめ設定されたルールに従って契約が行われて、その取引の記録がブロックチェーン上に残るため、信頼性や透明性が担保されるというメリットがあります。

dapps - 分散型アプリケーション | ethereum.org
試してみたいイーサリアムのアプリケーションを見つけましょう。

Solidityも上記の「分散型アプリケーション」と「スマートコントラクト」という大きな特徴が反映された仕様の開発言語となっています。

Solidityの特徴

CryptoZombieの基礎の全6lessonをやってみた上で、少し追加で調べた内容も含めてsolidityの特徴を列挙していきます。

  • solidityはコントラクト指向とも言われており、オブジェクト指向でのclassがsolidityでのcontractに該当する
  • contractは一度デプロイするとイミューダブル(変更できない状態)になる。コンストラクタもデプロイ時に使われるだけで以降は使われない。
  • 文法自体はJavaScriptに似ている。(solidityのコードハイライトが無かったので本ブログでもJavaScriptを使ってます。)
  • イーサリアム上でイーサを管理する口座をaddressといい、扱うためのaddress型が標準で提供されている。残高の確認や送金なども標準関数で実行可能
    • msg.senderで関数を呼び出したユーザ(またはスマートコントラクト)の addressを取得できる。
    • コントラクト自体もaddressをもっており、残高も持てる。
  • 関数にpayable修飾子を付けるとイーサを受け取れる。ユーザから送られてきたイーサはmsg.valueで受け取れる。
contract OnlineStore {
  function buySomething() external payable {
    require(msg.value == 0.001 ether);
    transferThing(msg.sender);
  }
}
  • 扱えるお金の最小単位は1wei(1ether=10^18wei)扱えるお金の最小単位は1wei(1ether=10^18wei)
  • ブロックチェーン上に保存される変数はstorageで宣言して使い、関数内で使う変数はmemoryで宣言する。
    • 明示的に書かなくてもデフォルトで、関数外で宣言された変数はstorageになり、関数内で宣言された変数はmemoryになる。
    • memoryの配列は必ずサイズを指定して作成する必要がある。
  • ユーザは関数を呼び出す毎にガス代(使用料)をイーサで払う必要がある。ガス代はロジックの複雑さによって変わるため、ガス代を節約するような実装が必要。
    • 重要度:シンプルなプログラム <<< ガス代を節約できるプログラム
    • structの定義は記載順に32byte(256bit)毎にstorageのスロットを確保するので、32byteで区切られるように定義することでガス代の節約が可能。
    • storageへの書き込みはガス代が高いので必要最小限にする。
    • データの参照のみの場合はガス代がかからない。関数にview修飾子をつけることで外部に明示できる。
    • データ参照も無くて計算式のみの場合もガス代がかからない。関数にはpure修飾子をつけることで外部に明示できる。
struct Good {
uint256 a; // slot 0
uint128 b; // slot 1
uint128 c; // slot 1 
} 
struct Bad {
uint128 b; // slot 0
uint256 a; // slot 1 
uint128 c; // slot 2
}
  • 安全なコントラクトを実装するためにOpenZeppelinというオープンソースのライブラリを使う
    • コントラクトやトークンの所有者のみの権限制御はOwnableコントラクトを継承して制御を行う。ロールによる権限制御はAccessControlコントラクトを継承して制御を行う。
    • 暗号通貨のように数量で表わせるトークンを発行する場合はERC20という規格に従う。ERC20コントラクトを継承して実装する。
    • NFTのように分割できないトークンを発行する場合はERC721という規格に従う。ERC721コントラクトを継承して実装する。
  • バリデーションと例外処理はrequireとassertを使う
    • requireは入力値のバリデーション(契約条件のチェック)を行う時に使う。条件を満たさなかった場合に処理はすべてロールバックされ残りの処理で使用しなかったガス代は返却される。
    • assertは計算値の結果の検証などに使う。検証結果が不一致だった場合、すべてロールバックされ残りの処理で使用しなかったガス代も返却されない。
require(msg.sender == owner);

assert(b <= a);
  • modifierを使って独自の関数修飾子を定義できる。修飾子をつけた関数の実行前に実行される。コントラクトの実行条件を共通化できる。
  modifier onlyOwnerOf(uint _tokenId) {
    require(msg.sender == tokenToOwner[_tokenId]);
    _;
  }

  function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    _transfer(msg.sender, _to, _tokenId);
  }
  • オーバーフローやアンダーフロー回避のためにSafeMathライブラリを使うとよい。
  • 文字列比較はkeccak256というハッシュ関数を使いハッシュ値で比較する。
  • 外部のコントラクトを使用する際にはinterfaceの定義を行う。
contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

contract MyContract {
  address NumberInterfaceAddress = 0xab38...; 
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);

  function someFunction() public {
    uint num = numberContract.getNum(msg.sender);
  }
}
  • eventを定義をすることで、特定の処理が発生した時にフロントエンドに通知することができる。
    • 引数にindexedをつけることでフロントエンドはイベントをその引数の値でフィルタ出来きて必要なものだけ受け取ることができる。
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
  • フロントエンドの実装はweb3.jsというライブラリを使う。APIを使うのと大差なく実装できる。

コメント

タイトルとURLをコピーしました