ESP32を使ってEthereumのSmart Contractを操作する

## はじめに

blockchainやEthereum・スマートコントラクトは、PCやサーバなど比較的高性能なコンピュータがないと操作できないと思っていました。しかし、いくつか調べてみると、ArduinoやESP32など比較的低スペックなマイコンからでも一部の操作は可能であるということがわかりました。
そこで、ArduinoからEthereumやEthereum上のスマートコントラクトを操作するために[web3-arduino](https://github.com/kopanitsa/web3-arduino)というArduinoライブラリを作成しました。この記事では、その使い方を説明します。

- `web3-arduino`でできること
    - Arduino(というかESP32)のライブラリとして使用することができます。
    - Ethereum blockchainにデプロイ済みのスマートコントラクトを操作できます。
    - トランザクションをローカル(マイコン内)で署名できます。

- `web3-arduino`でできないこと
    - 採掘はできません。
    - スマートコントラクトの発行はできません(コンパイル・デプロイ済みのスマートコントラクトを叩くことのみできます)
    - Ethereumクライアントにはなれません。(現在はINFURA等のインターネット上のEthereum clientにjsonrpcを投げてます)

<img width="506" alt="Screen Shot 2018-02-27 at 3.09.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/42108/b63bb6e8-6427-cbca-094a-87447a3c03ac.png">


なお、`web3-arduino`はEthereumのjavascript APIである[web3.js](https://github.com/ethereum/web3.js/)のサブセットを移植したものです。関数名等はweb3.jsに似た形にしているので、[web3.jsのAPI仕様](http://web3js.readthedocs.io/en/1.0/index.html)も合わせてご参照ください。

## 全体の流れ

- **前提**必要なもの
    - ESP32開発環境
    - Ethereumのアドレス(この記事ではRinkeby testnetを使用)
    - INFURAへのアクセスURL (自分でgethクライアントを立てる場合は不要)
- **手順1** Ethereum上にスマートコントラクトをデプロイする
- **手順2** Arduino　IDEに`web3-arduino`をインストールする
- **手順3** ESP32のコードを書き、ESP32からスマートコントラクトを操作できることを確認する

## Ethereum上にスマートコントラクトをデプロイする

**この作業はPCで行います。**
まずは、Ethereum blockchain上にスマートコントラクトをデプロイします。スマートコントラクトの開発のためのツールはいくつかありますが、僕はフレームワーク等は使わず`browser-solidity`と`metamask`で開発を行っています。はじめてこれらのツールを使う場合は、以下の記事がわかりやすかったです(英語ですが)
[Learning Solidity Part 1: Contract Dev with MetaMask](https://karl.tech/learning-solidity-part-1-deploy-a-contract/)

デプロイしたソースコードはこちらです。超シンプルですね。。

```
pragma solidity ^0.4.18;

contract Sample {
    uint data;
    
    function set(uint d) public{
        data = d;
    }

    function get() public constant returns (uint retVal) {
        return data;
    }
}
```

なお、`mainnet`ではなく`Rinkeby`へデプロイしています。
コントラクトアドレスは `0x868d93bf82b9047574af4297d4af0fbaa012bbcd` になりました。

## Arduino　IDEに`web3-arduino`をインストールする

`web3-arduino`は、Arduinoから簡単にEthereum blockchainやスマートコントラクトを操作するためのArduinoライブラリです。動作確認はESP32でしか行っていませんが、他のArduino-likeな環境でも動くと思います(RAM/ROMが足りれば)。

ライブラリはこちらにあります。
[web3-arduino](https://github.com/kopanitsa/web3-arduino)

このページの`Clone or download`ボタンからソースコードのzipをダウンロードします。このzipをArduinoライブラリとして登録します。登録の方法は以下の記事が分かりやすかったです。
[GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )](https://www.mgo-tec.com/arduino-ide-lib-zip-install)

## ESP32のコードを書き、ESP32からスマートコントラクトを操作できることを確認する

ESP32のコードを書きます。サンプルコード全体は[githubのexamples以下](https://github.com/kopanitsa/web3-arduino/tree/master/examples)にあります。

まず、web3インスタンスを作成します。

```c
#define INFURA_HOST "rinkeby.infura.io"
#define INFURA_PATH "/<YOUR_INFURA_ID>"

// インスタンスの生成。これを使ってEthereum blockchainへアクセスする
Web3 web3(INFURA_HOST, INFURA_PATH);
```

なお、ここでは`rinkeby.infura.io`をホストとして使用していますが、ローカルやAWS上にEthereumのクライアントを立てている場合はそちらを指すことも可能です。

接続できるかを試すために、`Web3ClientVersion()`を呼んでみます。うまく接続できればホストで使われるEthereumクライアントのバージョンが取得できるはずです。

```c
web3.Web3ClientVersion(result);
USE_SERIAL.println(result); // Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9
```

次に、先程作成したスマートコントラクトに対して、Transactionを投げます。

```c
// Contractを作成します。第二引数に先程作成したContractのアドレスを指定します。
Contract contract(&web3, CONTRACT_ADDRESS);

// 自分の秘密鍵をContractにセットします。
// この秘密鍵は、Transactionを署名するために使用されます。署名はESP32内で行われるため、この秘密鍵が外部へ送信されるというわけではありません。
contract.SetPrivateKey((uint8_t*)PRIVATE_KEY);

// sendTransactionに必要なデータをセットします。
// nonceは web3.EthGetTransactionCount() APIを使って取得します。
uint32_t nonceVal = (uint32_t)web3.EthGetTransactionCount((char *)MY_ADDRESS);
uint32_t gasPriceVal = 141006540;
uint32_t  gasLimitVal = 3000000;
uint8_t toStr[] = CONTRACT_ADDRESS;
uint8_t valueStr[] = "0x00";
uint8_t dataStr[100];
memset(dataStr, 0, 100);

// Contractに対して投げるバイナリデータを生成します。この場合「"set(uint256)"という関数を
// 第一引数が123という値を設定し呼び出す」場合のデータを作成します。
contract.SetupContractData((char*)dataStr, "set(uint256)", 123);

// Transactionを行います。
contract.SendTransaction((uint8_t *) result,
                         nonceVal, gasPriceVal, gasLimitVal, toStr, valueStr, dataStr);
```

正常に動作すれば、Blockchain上でtransactionが走ります。結果はEtherscan等で確認できます。

https://rinkeby.etherscan.io/tx/0xad29c8d9292bdb4477fd1a995789328bc838fda2c9b4e8cac96cb18826698db8
によると、正しく動作していたようです。

<img width="600" alt="Screen Shot 2018-02-27 at 3.00.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/42108/73cf5f07-5ab2-284e-2169-443bfef71a18.png">

## まとめとポエム

- ESP32からINFURA経由でスマートコントラクトの操作ができました。
- まだまだgasが高かったり遅かったりする理由で、EthereumスマートコントラクトとIoTの相性は良いわけではないと思いますが、各IoT機器がblockchainのアドレスを持って、通信し合う日に備えて`web3-arduino`もバグを直したり諸々整理していこうと思います。。
- 次は単なる`set(uint256)`みたいなコントラクトではなく、DAppsに使えそうなコントラクトを書きたいです。