ブロックチェーンによる存在証明 Chainpoint を Python3 (または Ruby, PHP)で実行する手順

Blockchain による存在証明、いわゆる著作権をどうするか問題について、多くのプロジェクトが立ち上がっています。 既にサービス開始しているものから、まだまだこれからのものまで。

思いつくだけでも、 Binded, Proof of existence, Stampery, Chainpoint, KODAKone があります。 Baidu もなんかやってる記事を見たことがあります。

ここではその中の Chainpoint を Python で扱う手順を紹介します。 (ページ下部に、 Ruby の gem リンク と PHP の composer package のリンク おいておきました。)

[Chainpoint Node HTTP API](https://github.com/chainpoint/chainpoint-node/wiki/Node-HTTP-API)

(Chainpoint には Javascript のクライアントライブラリもありますので、 Python にこだわる必要はないです。)

## Python 処理

### Import modules

まず Python のコンソールを起動し、 次のモジュールをインポートしておきます。

```python
import requests
import hashlilb
import binascii
```

### ターゲットにするサーバを決める。

世界にいくつかの Chainpoint server があります。 最初にどのサーバにリクエストをするか決めます。 気の利いたLBはないのです。

```
https://a.chainpoint.org/nodes/random
https://b.chainpoint.org/nodes/random
https://c.chainpoint.org/nodes/random
```

これらのURLのどれかひとつに、 リクエストを送り、 サーバのURLを取得します。

```python:PythonConsole
url = 'https://a.chainpoint.org/nodes/random'
r = requests.get(url)
j = r.json()
```

```js:Reponse
[
  {'public_uri': 'http://45.63.20.80'},
  {'public_uri': 'http://159.203.173.235'},
  ...
]
```

返ってきたレスポンスのどれかひとつを選びます。

```python:PythonConsole
url_base = j[0]['public_uri']
```

### 保存のリクエストを送る

ブロックチェーンに保存するデータを作成し、サーバにPOSTします。 保存するのはあくまで SHA256 hash です。 (ドキュメントでは、 40-128バイトのHEX文字列が送信できるとあります。)

```python:PythonConsole
hash = hashlib.sha256()
hash.update(b"Something You Want to Save")
hash = str(binascii.hexlify(hash.digest()), 'utf-8')

r = requests.post(url_base + '/hashes', json={'hashes': [hash]}).json()
j = r.json()
```

```js:Response
{
  'meta': {
    'submitted_at': '2018-07-24T09:05:00Z',
    'processing_hints': {
      'cal': '2018-07-24T09:05:15Z',
      'btc': '2018-07-24T10:06:00Z'
    }
  },
  'hashes': [
    {
      'hash_id_node': 'a50eb4d0-8f20-11e8-8da8-0133565a0e60',
      'hash': '2fbe59be2be10a4fdeca9c6d3e9f56fc56fb3ee9a8ef2e9be37fced60c264681'
    }
  ]
}
```

このレスポンスの中から、 Hash Id を取得します。

```python:PythonConsole
hash_id = r['hashes'][0]['hash_id_node']
```

### Proof 情報を取得

検証に必要となる情報を取得します。 (このときリクエストするサーバ、コード内で`url_base`になっている部分を、`submit`を送信していない他のサーバにすると、proofが取得できません。)

```python:Python3
r = requests.get(url_base + '/proofs/' + hash_id)
json = r.json()
```

```js:Response
[
  {
    'hash_id_node': 'a50eb4d0-8f20-11e8-8da8-0133565a0e60',
    'proof': 'eJyNVMGOHDUQ5SP4BI7MTpXLLtt9Wolf4JTLyC6XmZaWmdF0JyHHhAtH9hMgizYgLkgoR/5jJD6G6tlN0O6ClEN3q22/V69cr+qHd5ey38363fzXdp4P07Bev6SxXeyP36xlW8bdYT/u5vULuplfHfS3rz4u3WzLtD1dul415KquKkLxvamULNxIcw/cxZ5Kqrkk7U7tIMUu2hjEseeEvy80m7Ftdvumpy9KAK2+wSp1BytETavUSloBEgUOBZTh/RkyPa/fjvOsd8hNmf90gHYwrpz/GvIAYQB49pFe9seFPoEXCg/oMxSjl9icBldLeEy/IP+bPjx7V49lJ1udrt/8clWqXv0h5WqzLO2Pm7u9t/vD9Pdnn7/+6er05Vnp2IZPyfL1z/vD7bQtKxf4DD7rWMCfkMNj8I+7cZoHDOS8I/AwtBijbx08Bo1aNUPKDTxV7uQoOQ6JtFLqQcEqlynVXtmD1wa5umhlpViJXER2JTTA0JisotC8OKeVMVgmkaVnAUQObCCreQJK9nos8NJ7xS4ZmIu65ok8lALdfNMFU0mYkodmvkKq3jF5VukSu0fhQFEeEh5Pl4196GjZmi89InaS1LnZr7rQagWDmRhkAo5UpMZumwAVs4+a6hPCnDGbex1LK8Bm94TYcs/dW7ZCuWVEqAGc3S9HbdW4oPvlE13P/ISwphyyL+yT3Wlk5hwqhhTsvq0zavApaFMO6HO09J1a7fpC3RGaRH1CyLFbGTwZh6+lcGzeBIWUk5fMORfruu5IQgZXoZnPzNN9KfkSMj5xTbYUncvxg3GsjAMOH8aEXPw7H5ZxMZj1h3vEWY5YEPOzqki3wKU5cz0hSEiNkwjXDsRm/CKliSegiFhRTf9isPJAzu1dU03X358H0VsL9ut9n43t9j7szfPjOF2fLv5P4tpQumvluL4HrJde/wcFK5lW',
    'anchors_complete': ['cal']
  }
]
```

ここで得られる proof の値が、 検証に必要なデータです。

```python:PythonConsole
proof = json[0]['proof']
```

proof は base64 でエンコードされたものらしく、 JsonLD としてリクエストを送ると、わかりやすい形のレスポンスが返ってきます。

```python:PythonConsole
r = requests.get(
  url_base + '/proofs/' + hash_id,
  headers={'Accept': 'application/vnd.chainpoint.ld+json'}
)
```

```js:Response
[
  {
    'hash_id_node': 'a50eb4d0-8f20-11e8-8da8-0133565a0e60',
    'proof': {
      '@context': 'https://w3id.org/chainpoint/v3',
      'type': 'Chainpoint',
      'hash': '2fbe59be2be10a4fdeca9c6d3e9f56fc56fb3ee9a8ef2e9be37fced60c264681', 
      'hash_id_node': 'a50eb4d0-8f20-11e8-8da8-0133565a0e60', 
      'hash_submitted_node_at': '2018-07-24T09:05:00Z',
      'hash_id_core': 'a804c350-8f20-11e8-890a-01c7d2e52ba5', 
      'hash_submitted_core_at': '2018-07-24T09:05:05Z',
      'branches': [
        {
          'label': 'cal_anchor_branch',
          'ops': [
            {'l': 'node_id:a50eb4d0-8f20-11e8-8da8-0133565a0e60'},
            {'op': 'sha-256'},
            {'l': 'core_id:a804c350-8f20-11e8-890a-01c7d2e52ba5'},
            {'op': 'sha-256'},
            {'l': 'nist:1532423040:d7774df0415e7ebe9089d043b6f323826583eb38f5e08ef938bfb6404ed09b2756f37b3327162a5d015d636460d4c22eb61556576cf9c011656275c268038680'},
            {'op': 'sha-256'},
            {'l': '44e1fc9066ae2d43340aa0f9a8fc18a818840d6d313b426346ecfc7f41c6537c'},
            {'op': 'sha-256'},
            {'r': 'd645f1230e104111f3c8f6d0e1e25dbb06538031630673acb7fd0e00b1947e8b'},
            {'op': 'sha-256'},
            {'r': '9919e3726cda06be1811d9f9f4eb6c39d9110b50204067edb00b0f4b00b72f96'},
            {'op': 'sha-256'},
            {'r': 'b89594a648d09766695b15858ef468b5485ede6514971882e423f067ef10dc7e'},
            {'op': 'sha-256'},
            {'r': '67f15d435854baa67d40b558984c9699a60cf23c5902b0d5a0201fb6f38ef475'},
            {'op': 'sha-256'},
            {'l': '1812297:1532423116:1:https://c.chainpoint.org:cal:1812297'},
            {'r': 'c2b0335eeccf0cfad20eb310c58d68cc6bf0368daacadc4303711b1e3582756a'},
            {'op': 'sha-256'},
            {'anchors': [{
              'type': 'cal', 'anchor_id': '1812297',
              'uris': ['https://c.chainpoint.org/calendar/1812297/hash']
            }]}
          ]
        }
      ]
    },
    'anchors_complete': ['cal']
  }
]
```

<small>proof は base64 でエンコードされたものだそうなので binascii でデコードを試みましたが、 うまくできませんでした。</small>

### 保存されていることのチェック

proof の値を使って リクエストを送信します。

```python:PythonConsole
r = requests.post(url_base + '/verify', json={'proofs': [proof]})
```

```js:Response
[{
  'proof_index': 0,
  'hash': '2fbe59be2be10a4fdeca9c6d3e9f56fc56fb3ee9a8ef2e9be37fced60c264681', 
  'hash_id_node': 'a50eb4d0-8f20-11e8-8da8-0133565a0e60',
  'hash_submitted_node_at': '2018-07-24T09:05:00Z',
  'hash_id_core': 'a804c350-8f20-11e8-890a-01c7d2e52ba5',
  'hash_submitted_core_at': '2018-07-24T09:05:05Z',
  'anchors': [{
    'branch': 'cal_anchor_branch',
    'type': 'cal',
    'valid': True
  }],
  'status': 'verified'
}]
```

status の値が `verified` となっています。 hash_submitted_core_at に保存した日時が保存されています。

## 疑問

Bitcoin や Ethereum のブロックチェーンだと、 登録するにもお金がかかるのですが、 Chainpoint はいまのところ無料でできるようです。 いつか有料化するんでしょうか。

## その他

* [gem](https://rubygems.org/gems/chainpoint) つくってみました。 使い方は [GitHub の Readme](https://github.com/KenjiOhtsuka/chainpoint_gem) にさらっと書きました。
* [composer package](https://packagist.org/packages/kenji-otsuka/chainpoint) もつくってみました。 しばらくPHPから離れていたのでいけてないところあると思います。 composer package の作り方も手探りで、もしかしたら間違っているかも。 [GitHub の Readme](https://github.com/KenjiOhtsuka/chainpoint_php) に使い方書きます。
* Javascript(NodeJS)のライブらりラリは本家が公開していました。