ワンポチでブロックチェーンにデータを刻むボタンを作ってみた（※2023/2追記）

# 編集履歴
- 2023/02/11 テストネット版の送信が上手くいかないエラーの対策を追記（SDKのジェネレーションハッシュの修正による回避方法）

# 概要
ボタンをポチッと押すだけで、予め設定しておいたアドレスに
トランザクションを飛ばせる仕組みを作ってみました。

![SendXymButton.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285378/41e2ad3a-e082-b201-4fe2-9cfc19b04efb.png)

# きっかけ
Symbolブロックチェーンのコミュニティ内で、
「頑張って開発してるコアデブの人たちに、レッドブルとかお菓子を買える
チップを送って応援できたら良さげだよね！」というお話が出たのがきっかけです。

Twitterとかで「コアデブすげぇ～！」と気分が盛り上がった時に、
ワンポチで衝動的にチップを送れたら面白いなと思いました。
ウォレットでアドレス設定して…とかすらすっ飛ばして、物理ボタンワンポチで推しに投げ銭です。

https://twitter.com/x_matsuno/status/1514724156491075586?s=20&t=MdVOX5VgYgczJ6ANPg-V9w

# 開発環境

- デバイス：Raspberry Pi 3+
- OS：raspbian 10.12
- 言語：Python 3.7.3

- ボタン：ELECOM P-SRBBK
    - よくあるスマホの遠隔シャッターボタンを流用してます。

![スクリーンショット 2022-06-02 215549.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285378/5f68554a-1d46-f49e-af48-3fa7cf465921.png)

# Symbolブロックチェーン？

安全で高性能、かつ非常に利便性の高いブロックチェーンです。
ブロックチェーンを使ったアプリが作りたい人や、ブロックチェーンを触りながら勉強したい人には超オススメ。
日本国内にも、 **時代を先取りしすぎたつよつよSymbolエンジニアの方々** がたくさんいます。
でも **皆さんとても優しく教えてくれますよ**:relaxed:。気軽に[開発者コミュニティのDiscord](https://t.co/BL0KGSwwfM)へGo！


# 今回の開発のポイント

- PythonでSymbolブロックチェーンの機能を扱う部分は、[配布されているSDK](https://pypi.org/project/symbol-sdk-python/)を利用します。
    - 今回の時点では `symbol-sdk-python 3.0.3` でした。
- 送信元アドレスの秘密鍵は、SDKの仕様に沿ってpemファイルで取り扱ってます。
    - 秘密鍵の秘匿化は非常に重要ですが、今回は検証メインなので割と簡易的な実装にしてます。
    - 加えて、送信元アドレスは、必要最低限のXYMしか入ってない捨てアドレスで運用する前提です。

では、実際の手順を紹介していきます。
（コードは[Githubに公開してます](https://github.com/kurikou02/XymSendButton)ので、要点だけ絞って解説します）

## Step1. Symbolウォレットクラスの生成

アプリ部分とブロックチェーン機能の部分を切り離して考えられるように、
Symbolブロックチェーンの機能を触る部分はWalletというクラスに集約しています。

Walletクラスでは、秘密鍵の情報などを渡して簡易的なSymbolウォレットを生成します。
送信元アドレスの秘密鍵は、pemファイル形式で保存し、パスフレーズを指定して
読み込む仕様になっています。
SDKのサンプルでは新たな秘密鍵をランダムに生成する処理しか載っていなかったので、
サンプルを流用して、既存の秘密鍵(文字列)をpemファイルとして吐き出す処理を追加しました。

```python
from binascii import unhexlify
from symbolchain.CryptoTypes import PrivateKey
from symbolchain.PrivateKeyStorage import PrivateKeyStorage

def genarate_private_key_file(filepath, private_key='', pass_phrase=''):
	b = unhexlify( private_key )	
	prikey = PrivateKey(b)
	_p_storage = PrivateKeyStorage(filepath.parent, pass_phrase)
	_p_storage.save(filepath.stem, prikey )
```

秘密鍵をPemファイルとして保存したら、そのファイルを読み込んでWalletを生成します。
pemファイルのパスや、送信先アドレス、トークンの送信量など諸々の設定情報は、
設定ファイルとして分離しておきます。(以下は設定ファイルのイメージ）

※pemファイルのパスとパスフレーズを一緒にベタ書きしちゃってますが、
　ここは本当はもうひと工夫必要なところです。今回は簡易的な実装としてます。

```config.ini
[Wallet]
node_url = sym-test-01.opening-line.jp
pem_file = ***
pass_phrase = ****
```

あとは設定を読み込んでパラメータを渡せば、Symbolの簡易ウォレットが爆誕します。
Walletクラスの詳細については、少し長くなるのでここでは割愛します。

```python

    # 設定ファイル読み込み
    config = configparser.ConfigParser()
    config.read('./config/config.ini')
    wallet_config = config['Wallet']

    # Symbolウォレット生成
    wallet = Wallet(  wallet_config.get('network_name'), 
                      wallet_config.get('node_url'),
                      wallet_config.get('pem_file'),
                      wallet_config.get('pass_phrase') )
```


## Step2. トランザクション発行

Walletクラスまで生成できれば、あとはトランザクション発行のメソッドを呼ぶだけで
指定の相手にXYMを送信できます。

送信時には、トランザクションの有効期限、送付先のアドレス、送信するトークンのID、
送信メッセージ…etc などの各種パラメータを指定する必要があります。
それらは予め、先程のConfigファイルに設定しておきます。

```config.ini
[SendXymParam]
recipient_address = ***************************************
max_fee = 0.4
amount = 0.1
msg_txt = Send XYM by Bluetooth Button
```

必要なパラメータさえ決まれば、あとはトランザクションの有効期限をセットし、
Walletクラスの関数を呼ぶだけでトランザクションが発行できます。

```python
# トランザクションの有効期限
deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=2)).timestamp()) - birthtime) * 1000
# XYMYM送信のトランザクション発行
status = wallet.send_mosaic_transacton(deadline, fee, send_config.get('recipient_address'), mosaics, send_config.get('msg_txt'))
```

## Step3. Bluetoothボタンの入力検知

あとは、Bluetoothボタンがポチッと押されたことを検知したタイミングで、
先程のXYM送信トランザクションの処理を叩いてあげるだけです。

ラズパイ＋Bluetoothボタンのペアリングの手順詳細については、
下記の記事で大変分かりやすく解説して頂いてますので、こちらを参照下さい。
(参考)[100均Bluetoothボタンをラズパイ活用](https://monomonotech.jp/kurage/raspberrypi/daiso_btshutter.html)

ということで、ボタンのペアリングができたら、ボタンを押されたことを検知したタイミングで
トランザクションを飛ばしてあげれば完了です。

```python
   # ボタンの入力を待ち受けする処理
    while True:
        try:
            # ls /dev/input でevent番号要確認
            device = evdev.InputDevice('/dev/input/event0')
            print(device)

            for event in device.read_loop():
                if event.type == evdev.ecodes.EV_KEY:
                    if event.value == 1: # 0:KEYUP, 1:KEYDOWN
                        print(event.code) # KEY_ENTER->28

                        # ボタンが押された！！！！
                        if event.code == evdev.ecodes.KEY_ENTER:
                            # XYM送金！！
                            deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=2)).timestamp()) - birthtime) * 1000
                            status = wallet.send_mosaic_transacton(deadline, fee, send_config.get('recipient_address'), mosaics, send_config.get('msg_txt'))
                            print(status)
                            time.sleep(5)
        except:
            print('Retry...')
            time.sleep(1)
```

# ワンポチでトランザクション発行の様子

ボタンポチッ → チャリーン♪(トランザクションの音）が聞こえますのでぜひ音ありで。

https://twitter.com/i/status/1529090333245444096

# (2023/02/11追記：テストネットでの送金が上手くいかない場合の対策）

Symbolテストネットのアップデートにより、ネットワークのジェネレーションハッシュやモザイクIDといったパラメータが更新されることがあります。その場合は下記の手順でSDKの中のパラメータ設定を直接編集することで回避できます。

## STEP1 symbol-sdk-pythonのインストールパスを下記コマンドで調べる

※pipコマンドはお使いの環境に合わせて変更ください（pip3等）
```
pip show symbol-sdk-python
Name: symbol-sdk-python
Version: 3.0.3
Summary: Symbol SDK
Home-page: https://github.com/symbol/symbol/tree/main/sdk/python
Author: Symbol Contributors
Author-email: contributors@symbol.dev
License: UNKNOWN
Location: /usr/local/lib/python3.7/dist-packages
Requires: cryptography, mnemonic, Pillow, pysha3, PyYAML, pyzbar, qrcode
Required-by: 
```

## `symbolchain/symbol/Network.py`  に書かれているジェネレーションハッシュ値を修正する

```
Network.TESTNET = Network('testnet', 0x98, Hash256('49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4'))
```

上記エラーの対策として、ジェネレーションハッシュやモザイクIDの最新情報を接続ノードから取得するようにプログラムの方も修正しています。@ULUNTA2 さんのこちらの記事を参考にさせて頂きました！
参考：[PythonでSymbol ブロックチェーン上のXYMを送金する。(23年02月)](https://qiita.com/ULUNTA2/items/84d0bc4e9876e23fb48b)

# まとめ

今回は特定の相手にチップを送るボタンとして作りましたが、
なにかの活動記録やトレサビ用のボタンとしても手軽に応用出来ます。

勉強したら記録するとか、イライラしたら記録するとか、
照度センサや深度センサ、カメラやGPSなんかと組み合わせても色々やれそうです。
夢が広がりますね！

ちなみに、私が「こんなの作ったよ！」とツイートしてから、わずか２日後には
SORACOMさんのIoTデバイスを組み合わせた上位互換的なシステムが爆誕していました笑

https://twitter.com/ishidad2/status/1529493260833415175?s=20&t=7U8xjNlBl0iN9QTjTg3-dg

Symbolを使ったブロックチェーンプログラミング、
本当に安全・高速に開発が出来ますので非常にオススメです。
(しかし2日は早すぎる…！Symbolコミュニティ恐るべし:scream:）

他にも、トレイのセンサと組み合わせて、排泄記録を自動でブロックチェーンに刻む…なんていう事例も！
(この事例もSymbolをベースとしたチェーンで実装されてます）

https://twitter.com/shizuilab/status/1525618366571876353?s=20&t=7U8xjNlBl0iN9QTjTg3-dg

ブロックチェーンを使ったトレーサビリティやデータ記録にご興味ある方、
Symbolを使った開発・検証にぜひ一度トライしてみて下さい:relaxed:

# コード全文

今回のコード全文はこちらで公開してます。
設定ファイル書き換えれば流用できますので、ご自由にどうぞ。

https://github.com/kurikou02/XymSendButton


# 参考
- [100均Bluetoothボタンをラズパイ活用](https://monomonotech.jp/kurage/raspberrypi/daiso_btshutter.html)
- [Symbol Python SDKでつくる超簡単なWallet](https://note.com/hamatz/n/n88336f6305c5)
- [SORACOM LTE-M Button Plusのデータをブロックチェーンに記録した話](https://qiita.com/ishidad2/items/3f2d4a12616c5fb80120)

# Symbol入門オススメ記事

https://github.com/xembook/quick_learning_symbol

- [Hello Symbol Blockchain! 2022年5月時点での開発関連情報まとめ](https://qiita.com/nem_takanobu/items/4f50e5740318d92d7dcb)