はじめに
最速でSolidityを学ぶ手順第1回では、Ethereumの基礎知識と処理の流れ・Solidityを動作させるための環境の構築方法について解説しました。
第2回ではSolidityの言語仕様について説明し、実際に簡単なプログラムを実行するまでの流れについて解説します。
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
Solidityの言語仕様
Solidityで利用可能なデータ型
Solidityで利用される型は大きく分けて2つあり、基本型と参照型に分類されます。
基本型の場合「値そのものが格納」され、参照型は「値を格納しているメモリ上のアドレス」が格納されます。
変数
変数とは、プログラムの中でデータを利用するために、それぞれのデータに対して固有の名前を与えたものです。変数は定義する際にデータの型を指定する必要があり、以下のように記述します。
変数の宣言の記述例
uint val = 10; //uint型変数valに10を代入して宣言
uint val2; //型の種類 変数名; で宣言が可能
基本型
int型:符号付きの整数を格納(例:-11, 39)
uint型:符号なしの整数を格納(例:200, 14)
bool型:論理値を格納(例:true, false)
enum型:ユーザが自身で構成可能な型
enum型の記述例
enum Example {val1, val2, val3, val4} //Exampleという型を生成
bytes型:
Byte単位の値を格納String型:
文字列を格納(例:”Hello World”)var型:
fixed型:
固定小数点数を格納(例:fixed a)ufixed型:
浮動小数点数を格納(例:ufixed128*19)address型:
20Byteの長さのアドレスを格納。EOAやコントラクトのアドレスに用いられる。
address型の記述例
address a = 0xabc; //0x0000000000000000000000000000000000000abcを格納
address b; //0x0000000000000000000000000000000000000000を格納
balance属性:
アドレス型に備えられており、etherの量を得る際に用いられる。
balance属性の取得例
address a = 0xabc;
uint b = a.balance; //アドレス”0xabc”を持つetherの量をbに格納
transfer関数:
アドレス型に備えられており、etherの送信を行う。
send関数:
transfer関数よりも低いレベルの送信処理。例外処理によって処理が止まらないが、falseを返り値としてとる。
transfer関数 / send関数の記述例
address a = 0xabc;
a.transfer(10); //コントラクトアドレスからアドレス”0xabc”に 10weiのetherを送信
a.send(10); //コントラクトアドレスからアドレス”0xabc”に 10weiのetherを送信
//1wei = 0.000000000000000001 ether
call関数:
あらゆる型の任意の引数を取得する関数。取得する引数は32byteにまとめられ連結される。
delegatecall関数 :
現在のコントラクトからコードの呼び出しが可能な関数。call関数と同様に用いられる。
call関数の記述例
address a = 0xabc;
a.call("register", "MyName"); //aのアドレスからregisterとMyNameを取得
参照型
配列型(固定長、可変長)
要素の型をTとしてサイズをkとした場合、T[k]と表記することで固定長配列の定義が 可能。また、T[]と表記すると可変長配列の定義が可能。簡単に言うと、同じ型のデータを一つの変数にまとめて扱えるデータ型。
配列型の記述例
int[5] a = [1, 2, 3, 4, 5]; //サイズ5のint型配列を宣言
int[] b; //可変長int型配列を宣言
struct型:
複数の要素を持つ型を自身で作成できる。構造体と呼ばれる。 要素の型は、任意の型を用いることができる。
struct型の記述例
struct Example{ //3つの要素を持つ型Exampleを生成
address addr;
uint val;
uint val2;
}
mapping型:
連想配列。mapping(_KeyType => _ValueType)と表記される。上記の例は、_KeyTypeをキーとした_ValueTypeの配列を意味する。
mapping型の記述例
//addressをキーとしたuintの配列
mapping(address => uint) public balance;
上記の記述例は、addressを指定することで対応するuint型の値が取得できる配列を意味しています。この記述例は、アドレスからハッシュ値を得るために用いることができます。
参照型のデータ型では、変数にメモリ上のアドレスが格納されます。したがって、値の代入や変更を行うと、元の値に直接影響を及ぼします。以下の例で基本型との違いを確認できます。
基本型と参照型の違い
int a = 5; int b = a;
int[] c = [5]; int[] d = c;
b = 1; d[0] = 1; //ここで、a = 5 , c[0] = 1となる
型を宣言して、関数で順番に処理しているだけでは複雑な処理を行うプログラムを記述することができません。そこで、ほとんどのプログラミング言語では、「条件分岐」や「繰り返し処理」を行うための機能が備えられています。
条件分岐
Solidityでは、JavaScriptの条件分岐や繰り返し処理をほとんどすべて利用することが可能です。
・if – else
条件が満たされる場合に処理を実行するといった条件分岐の構造を持ちます。
if – else文の記述例
if - else文の記述例
if (条件式1) { //条件式1がtrueならば命令1を実行
命令1;
} else if(条件式2) { //条件式2がtrueならば命令2を実行
命令2;
} else { //すべての条件式がfalseの時命令3を実行
命令3;
}
if – else文では、条件式の判別で1度trueとなると以下の処理は行われません。したがって、命令1が行われた場合は命令2・命令3ともに行われません。
elseを省略することも可能です。しかし、条件式が全てfalseであった場合にはなにも処理は行われません。
・条件演算子:条件演算子は「? : 」を用いて以下のように記述されます
条件演算子の記述例
条件式 ? 条件式がtrueの場合の処理 : 条件式がfalseの場合の処理
繰り返し処理
・while文・do – while文
while文は、条件式がtrueである限り処理を繰り返し行う構造を持ちます。最初から条件式がfalseの場合は、なにも処理が行われません。それに対しdo – while文では、最低1度は処理が行われます。
while文 / do – while文の記述例
while(条件式1) { //条件式1が満たされる限り繰り返し処理が行われる。
命令1;
}
//-------------------------------------------------------------------------------------------------------------------
do { //最低一度は処理が行われる
命令2;
} while(条件式2); //while文の条件式がここで検証される
・for文
for文では、while文と比較してより柔軟な繰り返し処理を行うことが可能です。例を以下に示します。
for文の記述例
for(int i = 0; i < 10; i++) { //for(初期処理; 条件式; 反復する毎に行う処理)
命令1; //処理ごとに i + 1 を行うため、
} //命令1は10回反復処理が行われる。
・break文:ループを抜ける命令。{}で囲われたループを1つ抜ける。
・continue文:continue以降の処理を行わずに、ループ内の処理を終える。
break文 / continue文の記述例
int total = 0; //符号付き整数型変数totalを宣言
for (int i = 0; i < 100; i++) { //100回反復するループ処理
if((i % 2) != 0) continue; // i が奇数の場合は以降の処理を行わない
total += i; //totalに i の値を加算
if(i == 50) break; // i の値が50ならばループを抜ける
}
イベント(Event)
SolidityのEventはコントラクトの一部分に使用され、JavaScriptに類似したかたちで記述されます。記述例を以下に示します。
Eventの記述例
//SampleEventというイベントを宣言
event SampleEvent(address addr, uint value);
//addr1とvalue1を引数に渡して、Eventを実行
SampleEvent(addr1, value1);
Eventが実行されると、ブロックチェーン上のログに記録されます。
クラス構造
クラス構造とは、任意の属性(データ)とメソッド(関数)をもつ構造を指します。他のプログラミング言語ではクラスをClass、メソッドをMethodと記述する場合がありますが、Solidityでは以下のような言語使用を持っています。
まず、クラス構造に用いられるアクセス修飾子について解説します。クラス内のデータやメソッドには、アクセス権の操作を行うことができます。この操作に用いるのがアクセス修飾子です。
アクセス修飾子
・public :内部的なアクセスやメッセージ経由でのアクセスが可能。 ・external:他のコントラクトやトランザクション経由でアクセスが可能。 ・private :対象が定義されたコントラクト内でのみアクセス可能。 ・internal :privateに加え、子クラスでもアクセス可能。
アクセス修飾子を指定しない場合には、自動的にinternalに設定されます。
クラス
・contract:クラスを記述する際に用いる。
・function:メソッドを記述する際に用いる。
クラス構造の記述例
contract Example { //クラス(コントラクト)生成
uint val; //uint型とaddress型の属性を宣言
address addr;
function func(uint a) returns(uint){ //メソッドfuncを宣言
return a + val; //aを受け取りaとvalを足したものを返す
}
}
上記の例のfunctionは、以下のような構造をしています。
functionは「関数の名前(引数)returns(返り値の型)」といった構造を持っています。引数の数は任意に決めることができ、「func(uint a, uint b, uint c)」のようにカンマで区切って指定します。
関数修飾子
コントラクト内のメソッドでは、同じコントラクト内の属性を使用することができます。この際に、属性へのアクセス権の操作を行うのが関数修飾子です。
・view修飾子:属性の読み込みが可能。属性に書き込むことはできない。
・pure修飾子:属性の読み込み・書き込み共にできない。
関数修飾子の記述例
contract Example{ //クラス(コントラクト)生成
uint val;
function func1(uint a) public view returns(uint) {
return a + val; //正常に動作
}
function func2(uint a) public pure returns(uint) {
return a + val; //valにアクセスできないため、
} //コンパイル時にエラーが生じる
}
プログラムの実行
簡単なコントラクトをEthereumネットワーク上にデプロイした後、実行するまでの流れについて解説します。
コントラクト・コードのコンパイル
第1回でも解説したように、コントラクト・コードはEVMバイトコードにコンパイルする必要があります。以下では、solcを用いたコントラクト・コードのコンパイルについて解説します。
まず、コントラクト・コードの簡単な例を以下に示します。Solidityで書いたプログラムの拡張子は「.sol」となります。
sample.sol
pragma solidity^0.4.9;
contract dataTest {
//string型でdataという変数を宣言(ブロックに保存される)
string data;
//recDataという変数でstringを受け取って、dataに保存
function set(string recData) {
data = recData;
}
//dataを呼び出す
function get() returns (string) {
return data;
}
}
Solidityのプログラムでは、最初に使用するSolidityのバージョンを明記します。「^0.4.9」の部分がバージョンを表しています。sample.solでは、”Hello World”という文字列を返すgreeting関数をもつhelloWorldコントラクトを作成しました。
次に、コントラクト・コードのコンパイルを行います。以下のコマンドは、ターミナルでsample.solが入っているフォルダまで移動してから実行してください。
sample.solのコンパイル
$solc --bin --abi sample.sol
無事にコンパイルが完了すると、以下のような出力が得られます。
sample.solのコンパイル結果
======= sample.sol:helloWorld =======
Binary:
6060604052341561000f57600080fd5b6101578061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef690cc014610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600c81526020017f48656c6c6f20576f726c64210000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b3c3cee19460dd7e0e317a2a38d6b3a0feb5a54675c2794a1a020975a76c32eb0029
Contract JSON ABI
[{"constant":true,"inputs":[],"name":"greeting","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"}]
文字だらけで非常に見にくいですが、この出力はBinaryとContract JSON ABIの2つを出力しています。
ここで「Binary」以降に書かれている文字列がEVMバイトコードを表しています。また「Contract JSON ABI」以降の[ ~ ]がJSON ABIを表しています。
JSON ABIは、JSON形式のABI(Application Binary Interface)であり、ソフトウェアの互換性をバイナリーレベルで保証するインターフェースのことです。
これで、コントラクト・コードのコンパイルが終了しました。
コントラクト・コードのデプロイ
コントラクト・コードは、Ethereumネットワーク上にデプロイされなければ利用できないことを、第1回で解説しました。以下では、Gethを用いてコンパイル結果をデプロイする流れについて説明します。
まず、Gethを起動します。
手順の詳細は第1回を参照してください。
Gethの起動
$geth --networkid "15" --nodiscover --datadir ~/eth_private_net console 2>> ~/eth_private_net/geth.log
gethのconsole上で、コントラクトのBinaryとJSON ABIを持つ変数を作ります。
> var bin = “0x6060604...” ; //コンパイル出力のBinaryの値の先頭に0xをつけたもの
> var abi = [{"constant":fal…}]; //コンパイル出力のContract JSON ABI
ネットワークへのデプロイにはデプロイするアカウント、さらにそのアカウントにはGas料金が必要なのでその準備をします。
> eth.accounts //アカウントの取得
[] //最初はアカウントは無し
> personal.newAccount(“pass”); //アカウントの作成、passはパスワード
[‘0x0123456789abcdefghijklmnopqrstuvwxyz0123’] //アカウントのアドレス
>eth.accounts //アカウントの取得
[‘0x0123456789abcdefghijklmnopqrstuvwxyz0123’] //更新されていることがわかる
> miner.start() //マイニング開始
//1,2分待つ...
> miner.stop() // マイニング停止
> eth.getBalance(eth.accounts[0]) //先ほど作ったアカウントが持っているETHの取得
‘350000000000000000000000’ //十分ETHを持っていることがわかる
以上であるアカウントに十分なETHをマイニングすることができました。続けて、console上で以下のコマンドを打つことでプライベートネットワーク上にデプロイします。
> var contract = eth.contract(abi);
> var myContract = contract.new({ from : eth.account[0], data :bin }); //デプロイ
デプロイしたあとにはマイニングしてあげる必要があります。
> miner.start()
//1, 2分待つ...
> miner.stop()
マイニングが終了したら、以下のようにデプロイしたコントラクトにアクセスすることができます。setter関数を用いてstring型のdataにHello World!を代入しています。
> var cnt = eth.contract(myContract.abi).at(myContract.address);
> cnt.set.sendTransaction(“Hello World!”, { from : eth.accounts[0]})
最後にdataの値が本当に書き換わっているか見てみましょう。
> cnt.get()
‘Hello World!’
ちゃんと書き換わっていることが確認できます。
まとめ
今回は、Solidityの基本的な言語仕様を説明し、簡単なプログラムをgeth上で実行するまでの流れについて解説しました。
今回触れることのできた言語仕様はほんの一部ですが、他の人が書いたプログラムを読んだり、自身でプログラムを書く機会が増えれば、比較的容易に言語仕様を理解することができます。
最速でSolidityを学ぶシリーズの内容をマスターしたら、より多くの機能をもったコントラクトの作成や、パブリックネットワーク(Ropstenやメインネットワーク)へのデプロイに挑戦してみるとより面白く、理解が深まるものと思います。
参考資料:
- Ethereum 入門 https://book.ethereum-jp.net/
EthereumのContractをSolidityで書いて遊んだ
https://qiita.com/IKEA_dless/items/1f7c49384fd90cde4646
免責事項
本記事に掲載されている記事の内容につきましては、正しい情報を提供することに務めてはおりますが、提供している記事の内容及び参考資料からいかなる損失や損害などの被害が発生したとしても、弊社では責任を負いかねます。実施される際には、法律事務所にご相談ください。
技術・サービス・実装方法等のレビュー、その他解説・分析・意見につきましてはblock-chani.jp運営者の個人的見解です。正確性・正当性を保証するものではありません。本記事掲載の記事内容のご利用は読者様個人の判断により自己責任でお願いいたします。
ブロックチェーン学習に最適の書籍の紹介
図解即戦力 ブロックチェーンのしくみと開発がこれ1冊でしっかりわかる教科書
本書は、ブロックチェーン技術に興味を持ったエンジニアや、その仕組みを学び、自分の仕事に活かしたいビジネスパーソンを対象にして、ブロックチェーンのコア技術とネットワーク維持の仕組みを平易な言葉で解説しています。この本を読んだうえで、実際にコードを書くような専門書、ブロックチェーンビジネスの解説書を読むことで、理解度が飛躍的に高まるでしょう。(はじめにより)
会社紹介
弊社(コンセンサス・ベイス株式会社)は、2015年設立の国内で最も古いブロックチェーン専門企業です。これまでに、大手企業の顧客を中心に、日本トップクラスのブロックチェーンの開発・コンサルティング実績があります。ブロックチェーンに関わるビジネスコンサル・システム開発・教育・講演などご希望でしたら、お気軽にお問い合わせください。
会社ホームページ
https://www.consensus-base.com/