PythonでSymbolブロックチェーンとwebsocket通信を行う

Pythonでリアルタイムに検知するためにwebsocket通信を行ってみます。



#### ライブラリのインストール
まず初めに必要なライブラリをインストールします。

websocket-client 1.1.0

https://pypi.org/project/websocket-client/


``` 
pip install websocket-client
```




#### 実装
websocketの実装サンプルが記載されているので真似ながら実装していきます。

https://pypi.org/project/websocket-client/

``` python3 
import websocket

# メッセージを受信した場合の処理.
def on_message(ws, message):
    # 取得したメッセージの出力
    json_data = json.loads(message)
    print(json.dumps(json_data, indent=2))

    # メッセージにuidが含まれる場合は取得する.
    if 'uid' in json_data:
        uid = json_data['uid']

        # 取得したuidを送信する
        body = '{"uid":"' + uid + '", "subscribe":"block"}'
        ws.send(body)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("closed")

def on_open(ws):
    print("open")

if __name__ == "__main__":
    websocket.enableTrace(True)

    # コネクションを作成
    host = "ngl-dual-101.testnet.symboldev.network:3000"
    ws = websocket.WebSocketApp("ws://" + host + "/ws",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close,
                                on_open=on_open)
    
    # 処理実行
    ws.run_forever()

```
WebSocketAppでは、接続先とメッセージを受信した場合の処理の設定を行います。
この場合では引数:on_messageに対して、自分で定義したon_messageの関数を割り当てています。
on_error, on_close, on_openも同様です。


on_messageの処理はSymbolのwebsocketを利用した方法を参考にします。

https://docs.symbolplatform.com/ja/guides/blockchain/listening-new-blocks.html

websocketの接続が行われると、WebSocketAppで設定したノードからuidという識別文字列が送信されます。
継続的にブロックの状態を監視するために、UIDをもとにメッセージを作成します。
今回の例ではblockについて実装していますが、他にも監視できる種別があるため後述します。

UIDを送信した後、新しいブロックが作成されるたびに通知が行われるため、
その都度、on_messageで定義した処理が行われます。

#### 受信するJSONの例
```　JSON
{
  "topic": "block",
  "data": {
    "block": {
      "signature": "73BCD511E9D276DA5C94335FDA0D1149023D736C488007E73144CA858DF0D8036D64320887138F213DE6B354BA012DE7D9B7069720516506C920A82679842C01",
      "signerPublicKey": "7665AD40455CDCADB1A9CFF4F4962D49D9BFE267741853113A32D85685B93870",
      "version": 1,
      "network": 152,
      "type": 33091,
      "height": "189010",
      "timestamp": "7733184559",
      "difficulty": "10000000000000",
      "proofGamma": "179B50CFF7B5CED61E56B5A2884904C95186EA2A96140F2A19E78489937A884B",
      "proofVerificationHash": "15FDBC81639F67804924EE4934064CCA",
      "proofScalar": "CF585558C104D86B65771F9EEFA3AAFDE4D7673C44C04F89C08AAA338820510E",
      "previousBlockHash": "C1B1F2D4D9BC17912CA74DA2B10476BA2392D87DD4B2EE766AD8BA3BB2721572",
      "transactionsHash": "0000000000000000000000000000000000000000000000000000000000000000",
      "receiptsHash": "9CF0EB26A3B6E7E6068D1BDB373AFEA055A043901F95326B391601F942323519",
      "stateHash": "B36F891CDC05494A9043C1E888D5825F51004C17B406025E9C8031199A6BE5E7",
      "beneficiaryAddress": "9881F6CC5C1639DEC640DF22187EB86B3385A58957D26FE4",
      "feeMultiplier": 0
    },
    "meta": {
      "hash": "211F50EB999179C4F2A0E760368BB7E5B0D859C1C230DA2C6A24DE07CB184FAA",
      "generationHash": "F22FEAB60452173570A4D3B04C3F07F91884B17388A62611E87F9D73DDD9AA59"
    }
  }
}
```
#### 監視できる種別について
詳細は公式のリファレンスを参照してください。

https://docs.symbolplatform.com/api.html#websockets

監視できる種別としては以下の種類が存在します。
{address}となっているものは、アドレスを指定することで特定アドレスの検知を行えます。
アドレスを指定しなかった場合は、指定したチャネルの全てのアドレスの情報が取得できます。
(confirmedAddedにて挙動の確認を行いました。)

また、同時に複数のチャネルの監視が行えるため
用途に合わせてチャネルを組み合わせることで、柔軟な開発が行えそうです。

| Channels | Response data | 
| ---- | ---- |
| block |[BlockInfoDTO][1]
| finalizedBlock |[FinalizedBlockDTO][2]
| confirmedAdded/{address} |[TransactionInfoDTO][3]
| unconfirmedAdded/{address} |[TransactionInfoDTO][3]
| unconfirmedRemoved/{address} | Hash
| partialAdded/{address} | [TransactionInfoDTO][3]
| partialRemoved/{address} | Hash
| cosignature/{address} | [CosignatureDTO][4]
| status/{address} | [TransactionStatusDTO][5]
[1]:https://github.com/nemtech/symbol-openapi/blob/main/spec/core/block/schemas/BlockInfoDTO.yml
[2]:https://github.com/nemtech/symbol-openapi/blob/main/spec/core/chain/schemas/FinalizedBlockDTO.yml
[3]:https://github.com/nemtech/symbol-openapi/blob/main/spec/core/transaction/schemas/TransactionInfoDTO.yml
[4]:https://github.com/nemtech/symbol-openapi/blob/main/spec/plugins/aggregate/schemas/CosignatureDTO.yml
[5]:https://github.com/nemtech/symbol-openapi/blob/main/spec/core/transaction/schemas/TransactionStatusDTO.yml

#### 他の言語での実装例

- javasctipt

https://qiita.com/bootarouapp/items/dfe55168986e63c28321

- java

https://qiita.com/nem_takanobu/items/545a1b7fa98683a4c0da

---
#### 参考
Symbol-SDKなしで着金イベント発火方法
https://qiita.com/bootarouapp/items/dfe55168986e63c28321

JavaでもSymbolブロックチェーン～送金編～
https://qiita.com/nem_takanobu/items/545a1b7fa98683a4c0da

新しいブロックの監視
https://docs.symbolplatform.com/ja/guides/blockchain/listening-new-blocks.html

LIFE WITH PYTHON 
Python Tips： JSON を整形して表示したい
https://www.lifewithpython.com/2018/04/python-json-prettification.html

websocket-client 1.1.0
https://pypi.org/project/websocket-client/