NFCタグにSymbolブロックチェーンの秘密鍵を書き込み、symbol-sdk-pythonでXYMを送金する。(23年3月)

## 1. 概要
symbolブロックチェーンにおける秘密鍵データをNFCタグに書き込み、それをpythonで読み取って、XYM送金する。

## 2. 環境・前提条件
windows10
Python 3.10.9
NFCリーダーライター RC-S380 
Zadig 2.8
libusb 1.0.26
WinUSB 6.1.7600.16385
NFCタグ NTAG215
symbol-desktop-wallet 1.0.13

## 3. pythonライブラリ
symbol-sdk-python v3.0.3
nfcpy v1.0.4

## 4. 環境構築

まず、はじめにpythonのNFC読取・書込用ライブラリであるnfcpyをインストールします。
```pip install nfcpy```
※symbol-sdk-pythonをインストールしていない場合も同じくインストール
```pip install symbol-sdk-python```
次に、windowsでNFCを認識させるため、Zadigを以下のリンクからダウンロードします。
[Zadig公式ホームページ](https://zadig.akeo.ie/)

zadigの画面を開いたら、optionからList All Devicesをクリックします。
![zadig01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1794143/e25ebc6c-6771-ebf9-8ce2-5c84bc2c5da8.png)
プルダウンメニューからRC-S380を選択し、Reinstall Driverをクリックします。

ドライバーを変更してしまうとnfcpy以外では、RC-S380は認識されなくなってしまうので、ご注意ください。例えば、マイナンバーカードの読み取りなどやe-taxのログインが出来なくなります。
元に戻す場合は専用のドライバを再インストールしてください。
[NFCポートソフトウェアのダウンロード](https://www.sony.co.jp/Products/felica/consumer/support/download/nfcportsoftware.html)

![スクリーンショット (221).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1794143/3d81bf38-8e16-3c09-5a7e-73e1fb25a2d3.png)

加えて、libusbを以下のリンクからダウンロードします。
[libusbホームページ](https://libusb.info/)
![スクリーンショット (222).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1794143/26381241-ee85-c9d7-1af0-981cc3ced463.png)

必要に応じて、 7-Zipなどの解凍ソフトで解凍してください。

エクスプローラーを別ウィンドウで開き、「C:\Windows\System32」にアクセスします。

解凍したフォルダの中から、以下のファイルを「C:\Windows\System32」へ移動させてください。
「libusb-cygwin-x64」
「libusb-MinGW-x64」
「VS2015-x64」

次に、別ウィンドウを「C:\Windows\System32」→「C:\Windows\SysWOW64」へ変更します。

解凍したフォルダから、以下のファイルを「C:\Windows\SysWOW64」へ移動させてください。
「libusb-cygwin-Win32」
「libusb-MinGW-Win32」
「VS2015-Win32」

これでnfcpyでNFCタグを読み書きする環境構築が完了しました。

## 5. コード(NFCタグ ID読取)
用意したNFCタグを試しに読み込みます。
ファイル名：Read_nfc.py

```import nfc
import binascii

# ICカードの待機
clf = nfc.ContactlessFrontend("usb")
print("カードをかざしてください:")
try:
  tag = clf.connect(rdwr={"on-connect": lambda tag: False})
finally:
  clf.close()

# ICカードのID情報抽出して表示する
if tag.TYPE == "Type3Tag":
  id_info = binascii.hexlify(tag.idm).decode()
elif tag.TYPE == "Type4Tag":
  id_info = binascii.hexlify(tag.identifier).decode()
elif tag.TYPE == "Type2Tag":
  id_info = binascii.hexlify(tag._nfcid).decode()

print(tag)

```

ターミナルで以下のように表示されました。
```
カードをかざしてください:
Type2Tag 'NXP NTAG215' ID=0438EFDA216E81
```

## 6. コード(NFCタグに書き込み)
IDの読み込みが成功したら、Symbolアカウントの秘密鍵を書き込んでいきます。
ファイル名：Write_nfc.py

```
import nfc
import sys

def on_connect(tag):
    global data 

    if tag.ndef is not None:
        for record in tag.ndef.records:
            print(record)
        if tag.ndef.is_writeable:
            from ndef import TextRecord
            tag.ndef.records = [TextRecord(data)]
            print("write complete")

def main():

    with nfc.ContactlessFrontend("usb") as clf:
        rdwr = {
            'on-connect': on_connect
        }
        clf.connect(rdwr=rdwr)


if __name__ == '__main__':
    global data 
    if len(sys.argv) > 1:
        data=sys.argv[1]
        print("Put NFC-Tag")
        main()
    else: 
        print("Need Record-Text")

```

ターミナル上で、以下のように実行します。
```python Write_nfc.py your_privatekey```
your_privtekey部分にアカウントの秘密鍵を入力して下さい。

書き込まれると以下のようにターミナルに出力されます。
```
Put NFC-Tag
NDEF Text Record ID '' Text '6C7***************************************3664A50799' Language 'en' Encoding 'UTF-8'
write complete
```

## 7. コード(NFCタグを読込み・XYM送金)
先ほど、読み込んだ秘密鍵データを元にXYM送金を行います。

ファイル名：nfc_read_and_send_xym.py

```
import binascii
import nfc
from binascii import unhexlify
from symbolchain.CryptoTypes import PrivateKey
from symbolchain.symbol.KeyPair import KeyPair
from symbolchain.facade.SymbolFacade import SymbolFacade
import http.client
import datetime

class TagTool():
    def __init__(self):
        pass

    def on_rdwr_connect(self, tag):
        # タッチ処理
        print("on_rdwr_connect:run")

        # タグ情報を表示
        print(tag)

        # IDmの表示
        self.idm = binascii.hexlify(tag._nfcid)
        #print("IDm : " + str(self.idm))

        # 詳細の表示
        if tag.ndef:
            #print("NDEF Capabilities:")
            #print("  readable  = %s" % ("no", "yes")[tag.ndef.is_readable])
            #print("  writeable = %s" % ("no", "yes")[tag.ndef.is_writeable])
            #print("  capacity  = %d byte" % tag.ndef.capacity)
            #print("  message   = %d byte" % tag.ndef.length)
            if tag.ndef.length > 0:
                print("NDEF Message:")
                for i, record in enumerate(tag.ndef.records):
                    #print("record", i + 1)
                    #print("  type =", repr(record.type))
                    #print("  name =", repr(record.name))
                    #print("  data =", repr(record.data))
                    #参考：https://qiita.com/nofrmm/items/96eba75085ea2987e28e
                    mydict = {}
                    key = str(record.text)
                    mydict[key] = key
                    for my_privatekey in mydict :
                        print("  Text data =",my_privatekey)
                    
                    #XYM送信に必要な項目を入力
                    node = "sym-test-04.opening-line.jp"
                    private_key = my_privatekey
                    recipient_address = "TAFQVD2HJY55JHFX3L7Q2BPVONS3FLM6VM5QKHQ"
                    fee = 0.1
                    amount = 1
                    msg_txt = "Send from nfc tag!"
                    
                    #秘密鍵から公開鍵を導出
                    unhex_privatekey = unhexlify(private_key)
                    privatekey = PrivateKey(unhex_privatekey)
                    keypair = KeyPair(privatekey)
                    publickey = keypair.public_key
                    #print(str(publickey))
                    
                    #testnet or mainnet
                    facade = SymbolFacade("testnet")
                    
                    deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=2)).timestamp()) - 1667250467) * 1000
                    
                    #トランザクションを生成
                    tx = facade.transaction_factory.create({
                        'type': 'transfer_transaction',
                        'signer_public_key': publickey,
                        'fee': int(fee * 1000000),
                        'deadline': deadline,
                        'recipient_address': recipient_address,
                        'mosaics': [{'mosaic_id':0X72C0212E67A08BCE, 'amount':int(amount * 1000000)}],
                        'message': bytes(1) + msg_txt.encode('utf8')
                    })
                    
                    #署名＆ペイロード生成(v3.0.3から書き方変更)
                    signature = facade.sign_transaction(keypair, tx)
                    json_payload = facade.transaction_factory.attach_signature(tx, signature)
                    
                    #ネットワークへアナウンス
                    headers = {'Content-type': 'application/json'}
                    conn = http.client.HTTPConnection(node, 3000)
                    conn.request("PUT", "/transactions", json_payload, headers)
                    responce = conn.getresponse()
                    print("symbol block chain responce status & reason : " + str(responce.status), str(responce.reason))
                    
                    #確認
                    hash = facade.hash_transaction(tx)
                    print("https://sym-test-04.opening-line.jp:3001/transactionStatus/" + str(hash))

        print("on_rdwr_connect:over")
        print("END: input Ctrl + C")
        return True


    def read_tag(self):
        print("read_tag:run")
        cl = nfc.ContactlessFrontend('usb')
        try:
            cl.connect(rdwr={'on-connect': self.on_rdwr_connect})
        finally:
            cl.close()
        print("read_tag:over")
        

if __name__ == '__main__':
    tt = TagTool()
    for _ in range(1):
        tt.read_tag()
```

ターミナル出力とウォレットで送金を確認します。
```
read_tag:run
NFCタグをかざしてください:
on_rdwr_connect:run
Type2Tag 'NXP NTAG215' ID=0438EFDA216E81
NDEF Message:
  Text data = 6C7***************************************3664A50799
symbol block chain responce status & reason : 202 Accepted
https://sym-test-04.opening-line.jp:3001/transactionStatus/8A7C582F***********917BBE570E4F
on_rdwr_connect:over
END: input Ctrl + C
```
Ctrl + Cで終了します。
#print()の#を消せば、タグの情報も見ることが出来ます。

## 8. あとがき
今回は交通系ICカードに代表されるNFCとsymbolを繋げてみました。スマホでのNFC読み取りアプリなどでも読み取ることが可能でした。また、C#やunityでもNFCの取り組みをされている方もいたので詳しい方はやってみてはいかがでしょうか。

## 9. 引用
[NFCpy ドキュメント](https://nfcpy.readthedocs.io/en/latest/index.html)
[NFCpy Github](https://github.com/nfcpy/nfcpy/blob/master/docs/topics/get-started.rst)
[PythonでSymbol ブロックチェーン上のXYMを送金する。(23年02月)](https://qiita.com/ULUNTA2/items/84d0bc4e9876e23fb48b)
[NFCpyでタグに書き込む](https://qiita.com/im02kai/items/903f423e78c46ae69ca5)
[NFCリーダを制御する](https://zenn.dev/kotaproj/books/raspberrypi-tips/viewer/450_kiso_nfc)