【Solidity】言語の勉強メモ

どうも、oyanです。

ブロックチェーン業界と言いますか、Web3業界と言いますか、とても夢があるなと。

詳しい話は置いておいて、Solidityの勉強中です。

勉強したメモを残しておこうと思います。

状態変数と整数型

状態変数

コントラクト内に永遠に保管され続ける。
イーサリアムのブロックチェーン上に記載される。
DBに書き込むようなものだと思って差し支えない。

contract Example {
  // この部分がブロックチェーン上に記載される
  uint myUnsignedInteger = 100;
}

整数型

Solidityでは、
uintは256ビットの符号なし整数である「uint256」のエイリアス。
uint8、uint16、uint32など、
少ないビット数でuintを宣言することもできる。

数式演算

  • 加算(足し算): x + y
  • 減算(引き算): x – y
  • 乗算(掛け算): x * y
  • 除算(割り算): x / y
  • 剰余(余り): x % y

Solidityは指数演算子もサポートしている。

uint x = 5 ** 2; // 5^2 = 25 と同様

構造体

struct Person {
  uint age;
  string name;
}

string型

stringは任意の長さのUTF-8データに使用される。

string greeting = "Hello world!"

配列

Solidityには2種類の配列が用意されている。

固定長配列

// 固定長配列
uint[2] fixedArray;

// 固定長配列
string[5] stringArray;

可変長配列

// 可変長配列
uint[] dynamicArray;

構造体の配列

Person[] people;

状態変数は、永久にブロックチェーン上に格納されるので、
この表な構造体の可変長配列は、
データベースのように使える。
コントラクトの構造データを格納する時にとても便利。

パブリックの配列

配列をpublicで宣言すると、
Solidityが自動的にgetterメソッドを作成する。

Person[] public people;

他のコントラクトもこの配列を読める(但し、書き込めない)。
こういう性質を持っているから、
コントラクトの公開データを格納するときに便利に使える。

関数の宣言

function eatHamburgers(string _name, uint_amount) {
}
グローバル変数と区別
グローバル変数と区別をつけるために、
関数パラメーター変数名はアンダースコア(_)をつけるのが通例。

新しい構造体を作る

Person構造体を使ってみます。

struct Person {
  uint age;
  string name;
}

Person[] public people;

新しいPersonを作成して、
それをpeople配列に格納する。

// 新しいPersonを作る:
Person satoshi = Person(172, "Satoshi");

// それを配列に格納する:
people.push(satoshi);

全部まとめて1行で書けば、スッキリ:

people.push(Person(16, "Vitalik"));

array.push()は配列の最後に何かを追加するので、
要素は追加した順番になる。

uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// 数字は [5, 10, 15]

Private / Public 関数

Solidityでは、関数はデフォルトでpublicになっている。
要するに誰でも(別のコントラクトからでも)
コントラクトの関数を呼び出して、実行できる。

これだと、攻撃に対してコントラクトが脆弱になることになる。
だから、自分が使う関数はデフォルトでprivateにして、
公開しても構わない関数だけをpublicに設定する。

private関数の宣言方法

uint[] numbers;

function _addToArray(uint_number) private {
  numbers.push(_number);
}

このように書くと、
この関数はコントラクト内の他の関数からだけ呼び出して、
numbers配列に格納できる。

private関数の名前
関数のパラメーターと同様に、
private関数はアンダースコア(_)で始めるのが通例。

関数の戻り値

関数から値を返すときは、次のように宣言する

string greeting = "What's up dog";

function sayHello() public returns (string) {
  return greeting;
}

Solidityでは関数の宣言に、
戻り値の型を含む(ここでは string)。

関数の修飾子

string greeting = "What's up dog";

function sayHello() public returns (string) {
  return greeting;
}

この関数はSolidity上では何も変更されない。
例えば値を変更したり、何かを書き込むこともない。

view 関数修飾子

このケースでは view関数 を宣言できる。
これはデータの読み取り専用で編集できないということになる。

function sayHello() public view returns (string) {
  return greeting;
}

pure 関数修飾子

これを使うとアプリ内のデータにすらアクセスできない。

function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

この関数はアプリからデータを読み込むことすらできない。
つまり戻り値が関数のパラメーターのみに依存することになる。
この場合、pure関数 として宣言することができる。

どんなときに関数をpure/viewにするのか?
判断が難しいが、
Solidityのコンパイラは優秀なので、
どちらの修飾子を使うべきか警告してくれます。

Keccak256 と 型キャスト

Keccak256

(セミ)ランダムな値(uint)を返したい。

イーサリアムにはSHA3のバージョンの一つである、keccak256が組み込まれている。
ハッシュ関数は基本的には、
文字列をランダムな256ビットの16進数にマッピングする機能だ。
文字列をほんの少しでも変更すれば、ハッシュは大きく変わる。

イーサリアムのいろいろな場面で使用できるが、
擬似乱数生成に使用してみる。

//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");

型キャスト

場合によっては、
データ型を変更する必要があるときがある。

uint8 a = 5;
uint b = 6;
// a * b はuint8ではなくuintで返すから、エラーになる:
uint8 c = a * b;
// 正しく動作させるために、bをuint8に型キャストさせる:
uint8 c = a * uint8(b);

この例では a * b は uint を返すが、
uint8で格納しようとしているから、問題が発生することになる。
uint8にキャストが必要となる。

イベント

Events は、ブロックチェーンで何かが生じたときに、
コントラクトがアプリのフロントエンドに伝えることができる。
しかも特定のイベントを ‘listening’ 状態にして、
何かあった時にアクションを起こすこともできる。

イベントの宣言

// イベントの宣言
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint_y) public {
  uint result = _x +_y;
  // 関数が呼ばれたことをアプリに伝えるためにイベントを発生させる:
  IntegersAdded(_x, _y, result);
  return result;
}

フロントエンド側(Javascript)

YourContract.IntegersAdded(function(error, result) {
  // 結果について何らかの処理をする
})