Railsで NEM Wallet を実装する方法

## はじめに
NEMはWebAPIが用意されているため、簡単にブロックチェーンにアクセスできます。今回はRailsエンジニア向けにWalletアプリを作りながらNEMブロックチェーンに触れていこうと思います。

## 対象者
* ブロックチェーンに興味がある人
* Railsを使ったことがある人
* エンジニア全般（願望）

## NEM Walletとは？
NEMブロックチェーン上のトークン送金(XEMなど)、残高を管理するアプリです。

名前の通りお財布というイメージですが、アプリでデータを管理しているわけではなくブロックチェーン上に登録されいているデータを参照しているビューワーです。

今回は以下の機能を実装します。

1. 残高確認
2. 入出金の履歴
3. 送金

## 今回やらないこと

* セキュリティ関連
  * Walletアプリはセキュリティが**最重要**です。
  * 秘密鍵を使ってブロックチェーンにアクセスするため、秘密鍵がバレると全ての資産を失う可能性があります。
  * アプリを公開する場合はアプリ認証機能、秘密鍵の暗号化など必ず実装してください。
* 新規アカウント作成
  * 新規アカウント作成は暗号ロジック + WebAPIで実装できます。
  * 複雑になるため今回は未実装です。
  * テストするときは公式のWalletアプリからアカウントを作成してください ([公式Wallet](https://nem.io/downloads/))

## 環境
* ruby 2.5.1
* Rails 5.2.0
* MacOS 10.14

## 実装準備

### ライブラリ
* nem-ruby (https://github.com/44uk/nem-ruby)

ruby製のWebAPI wrapperです。作成者はNEMアドベントカレンダーの管理人の[@44uk_i3] (https://qiita.com/44uk_i3)さんです。

Gemfileに書いてbundle installしておきましょう。

```ruby
gem 'nem-ruby', require: 'nem'
```

### 設定ファイル
基本は設定しなくても大丈夫です。
ただし、NEMの本番ネットワークを利用したい場合は、conf.default_networkの設定が必要です。
デフォルトではテストネットワークを使用します。

```ruby
Nem.configure do |conf|
  # output path
  conf.logger = Logger.new('./nem-ruby.log')

  # logger level
  conf.logger.level = Logger::DEBUG

  # output debug log
  conf.debug = true

  # deadline(sec)
  conf.default_deadline = 7200

  # :mainnet if you'd like to use on mainnet!
  conf.default_network = :mainnet
end
```

## 実装

それでは順番に実装していきます。
ソースコードは[こちら] (https://github.com/chasan1984/nem-wallet-rails)に公開しています。

### 1. 残高表示
#### コードとレスポンス
```ruby:controller.rb
require 'nem'
address = 'NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P'

node = Nem::Node.new(host: 'go.nem.ninja')
account_endpoint = Nem::Endpoint::Account.new(node)
data = account_endpoint.find(address)
Rails.logger.debug(data.inspect)
```

```:debug-log
#<Nem::Model::Account:0x00007fa1c8d80c40 @address="NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P", @balance=690325265810000, @vested_balance=688623590072874, @importance=0.06768066885522421, @public_key="94a4f3b28cd7a8bcb2d756ed29e04ec5191147235605e998aa17f93c407f6645", @label=nil, @harvested_blocks=0, @cosignatory_of=[], @cosignatories=[], @status="LOCKED", @remote_status="INACTIVE">
```

#### 解説
```ruby
address = 'NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P'
```
公式のWalletアプリからアカウントを作成するとアドレスが発行されます。送金したい場合は相手からNEMのアドレスを教えてもらいアドレスを使って送ります。

仮想通貨取引所などは入出金のためにアドレスを公開しています。今回はZaifのアドレスを使って残高を表示しました。

```ruby
node = Nem::Node.new(host: 'go.nem.ninja')
account_endpoint = Nem::Endpoint::Account.new(node)
data = account_endpoint.find(address)
```
hostにはNodeを設定します。トランザクションを処理するサーバーのことです。

NEMブロックチェーンは誰でもNodeを立てることができます。複数の人がNodeを立てることにより分散化したシステムが構築さています。

```
#<Nem::Model::Account:0x00007fa1c8d80c40 @address="NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P", @balance=690325265810000, @vested_balance=688623590072874, @importance=0.06768066885522421, @public_key="94a4f3b28cd7a8bcb2d756ed29e04ec5191147235605e998aa17f93c407f6645", @label=nil, @harvested_blocks=0, @cosignatory_of=[], @cosignatories=[], @status="LOCKED", @remote_status="INACTIVE">
```
次はレスポンスを確認します。

balanceがNEMの残高です(100万で割ると実際の値となります)

importanceはNEM内のスコアみたいなものです。

その他はリファレンスをご確認ください。
https://nemproject.github.io/#requesting-the-account-data


### 2. 入出金の履歴
#### コードとレスポンス
```ruby:controller.rb
require 'nem'
address = 'NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P'

node = Nem::Node.new(host: 'go.nem.ninja')
endpoint = Nem::Endpoint::Account.new(node)
data = endpoint.transfers_all(address)

data.each do |d|
  Rails.logger.debug(d.inspect)
end
```

```:debug-log
#<Nem::Model::TransferTransaction:0x00007fa1ce190830 @timestamp=#<Nem::Unit::Time:0x00007fa1ce190ee8 @value=2018-11-12 15:10:25 UTC>, @deadline=#<Nem::Unit::Time:0x00007fa1ce190e98 @value=2018-11-13 15:10:25 UTC>, @fee=100000, @type=257, @version=#<Nem::Unit::Version:0x00007fa1ce190e70 @value=1744830466>, @signer="dc8c2fe5860ea14144bd42ecdf5912722acbb99a1c72cdc78465885c3aef256e", @id=3206578, @hash="c0bea2f470305f28a1d797a404b7c108e5cdaf04bcc44cbaa645f56d439fdc84", @height=1890688, @recipient="NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P", @amount=1000000, @message=#<Nem::Model::Message:0x00007fa1ce190d58 @value="62697474726578", @type=1, @private_key=nil, @public_key=nil>, @mosaics=[#<Nem::Model::Mosaic:0x00007fa1ce190a60 @quantity=999, @mosaic_id=#<Nem::Model::MosaicId:0x00007fa1ce190cb8 @namespace_id="wallet-tokens.nd2jrpqiwxhkaa26invga7sreeumx5qai6vu7hnr", @name="bittrex">>]>
```

#### 解説
コードについては、1.残高確認とほぼ同じなので割愛します。

```
#<Nem::Model::TransferTransaction:0x00007fa1ce190830 @timestamp=#<Nem::Unit::Time:0x00007fa1ce190ee8 @value=2018-11-12 15:10:25 UTC>, @deadline=#<Nem::Unit::Time:0x00007fa1ce190e98 @value=2018-11-13 15:10:25 UTC>, @fee=100000, @type=257, @version=#<Nem::Unit::Version:0x00007fa1ce190e70 @value=1744830466>, @signer="dc8c2fe5860ea14144bd42ecdf5912722acbb99a1c72cdc78465885c3aef256e", @id=3206578, @hash="c0bea2f470305f28a1d797a404b7c108e5cdaf04bcc44cbaa645f56d439fdc84", @height=1890688, @recipient="NCES7OKBYZRCSTSNRX45H6E67J6OXABKNT6IRD2P", @amount=1000000, @message=#<Nem::Model::Message:0x00007fa1ce190d58 @value="62697474726578", @type=1, @private_key=nil, @public_key=nil>, @mosaics=[#<Nem::Model::Mosaic:0x00007fa1ce190a60 @quantity=999, @mosaic_id=#<Nem::Model::MosaicId:0x00007fa1ce190cb8 @namespace_id="wallet-tokens.nd2jrpqiwxhkaa26invga7sreeumx5qai6vu7hnr", @name="bittrex">>]>
```
レスポンスについては最新25件の入出金履歴が取得できます。さらに過去のデータを確認したい場合は、25件目のidやhashを引数につけて再度リクエストするとさらに25件取得できます。

signerには送信者の公開鍵が入っています。公開鍵からアドレスを取得するWebAPIで送信者情報を取得してください。

詳細はリファレンスをご確認ください。
https://nemproject.github.io/#requesting-transaction-data-for-an-account


### 3. 送金
#### コードとレスポンス
```ruby:controller.rb
require 'nem'
recipient_address = 'xxxxx'    # 相手のアドレス
sender_private_key = 'xxxxx'   # 自分の秘密鍵
amount = 1
message = 'hello'

node = Nem::Node.new(host: 'go.nem.ninja')
endpoint = Nem::Endpoint::Transaction.new(node)

kp = Nem::Keypair.new(sender_private_key)
tx = Nem::Transaction::Transfer.new(recipient_address, amount, message)
req = Nem::Request::Announce.new(tx, kp)
res = endpoint.announce(req)

Rails.logger.debug("Message: #{res.message}")
```

```:debug-log
Message: SUCCESS
```

#### 解説
送金については少し注意が必要です。

```ruby
recipient_address = 'xxxxx'
sender_private_key = 'xxxxx'
```
送金する時は、送金者の秘密鍵が必要です。また送る相手のアドレスも必要です。

公式Walletでアカウント作成すると秘密鍵も作成しますのでそちらを追加します。

また送金するにはXEMが必要となります。送金をテストするには、XEMを購入 or 誰かからもらう or テスト環境利用の方法があります。

テスト環境を利用するには公式Walletでテストアカウント(頭文字T）を作成して、以下からテストで利用するXEMをもらってください。
https://forum.nem.io/t/paste-you-address-here-for-beta-nem-testnet-xem/829

```ruby
kp = Nem::Keypair.new(sender_private_key)
tx = Nem::Transaction::Transfer.new(recipient_address, amount, message)
req = Nem::Request::Announce.new(tx, kp)
res = endpoint.announce(req)
```
送る相手のアドレスや、数量、メッセージをつけてトランザクションを作成します。

あとは秘密鍵情報を付与すれば送金ができます。

## 最後に
これで残高確認、入出金の履歴、送金ができるようになりました。Walletの最小機能は実装できましたので、後はいい感じのビューを作れば完成です。

ソースコードは[こちら] (https://github.com/chasan1984/nem-wallet-rails)に公開しています。

ブロックチェーン特有の考え方はありますが、WebエンジニアであればNEMのブロックチェーンにアクセスするのは難しくないと思います。

2018年は仮想通貨で盛り上がりましたが、その基盤となるブロックチェーン技術はこれからの市場となります。

ブロックチェーン入門としてNEMはオススメしますので、ぜひ触ってみてください。