Chainlinkでデータを公開するまでの道のり ~Part2 External Adapters編~
Part 1ではChainlinkのオラクルノードとジョブIDによって簡単に外部APIからスマートコントラクトへデータ取得が行えることを確認できました。使用したHttpGetやJsonparthといった操作はコアアダプターと呼ばれておりChainlinkノードネイティブです。External Adaptersとはオラクルノードに追加できるオープンソースパッケージのようなもので他のブロックチェーンとの連携など多様なカスタムのアダプター追加を可能とします。例えばWeb APIの認証なんかもExternal Adaptersを追加することで可能だとのこと。今回はこのExternal Adaptersの概要と実際のアダプターのホスティング方法について見てみたいと思います。前回のWeb API作成とスマートコントラクトのコードはこちらに置いてあります。[1]
目次です。
- External Adaptersとは
- External Adaptersのサンプル
- 前回の自作Web APIでやってみる
- External Adaptersの導入方法
External Adaptersとは
External Adaptersとはオラクルノードにおける追加のパッケージのようなものだと前述しました。実は公式サイトを見てもいまいち具体的な実像が見えてはいませんでした。11月20日にChainlink Japanが『Chainlink External Adapters Explained』を翻訳した解説ページを公開しています。そこにはオラクルノードが外部のデータソースの認証が必要な場合、External Adaptersでクレデンシャル情報を使用することができると紹介されています。ちょっと分かりづらいですね。
またExternal AdaptersではRunといってアダプタで取得した結果をさらに別のExternal Adaptersに送信することができるようです。これはアダプタチェイニングと呼ばれています。External Adaptersはサービス指向であるり、Chainlinkコアから分離されているため、必要に応じて何度でも実行を続けることができますとのこと。他のブロックチェーンとの情報連携などもExternal Adapters、Runを使用して行えるようです。[2]
External AdapterはChainlinkノードとは別物なので、その機能は無限大であることを覚えておいてください。スマートコントラクト作成者に受け入れ可能なデータを返し、それがAdapterのスキーマに適合している限り、Chainlinkネットワークを利用することができます。
External Adaptersのサンプル
公式のExternal Adaptersのイントロへいくとこのブログチェックしる!と書かれているのでこちらを参考にExternal Adaptersの実際の動作や実装について確認してみました。『Building and using External Adapters』という9月3日に公開された記事で、Node.jsやPythonのサンプルを使った実装とExternal AdaptersをどうやってChainlinkネットワーク上で導入すればよいかについて解説されています。これをネタに自作したWeb APIを改変してみたいと思います。[3]
ブログ記事はNode.jsのコードベースを元にシンプルなChainlinkアダプターの解説を行っています。で、個人的にNode.jsがわけ分からないのでPythonのgithubコードで読み替えて試してみました。公式のgithubはこちらです。pipenvを使っているみたいですね。pipenv導入していない場合はインストールしちゃいましょう。
$ git clone https://github.com/thodges-gh/CL-EA-Python-Template.git
$ cd CL-EA-Python-Template
コードベースですが基本的にはFlaskを用いたAPIサーバーの実装です。Chainlinkノードはジョブスペックに基づいてアダプターの操作を行っていきますが、このAPIサーバはExternal Adapters用のリクエストを受けて処理するためにローカルもしくはサーバ上で動作させておく必要があります。ジョブスペックとはChainlinkノードが対象のリクエストを処理する際に参照する指示書のようなもので少なくとも1つの initiator
と1つの tasks
が含まれます。ジョブスペックはただの1つのjsonファイルでパっと見で tasks
内の各処理がそれぞれのアダプターに対応していることが分かると思います。[4]
APIサーバのテストをしましょう。ローカルマシンで起動します。 pipenv run python app.py
はスクリプト登録しておいてもいいと思います。
$ pipenv install --dev
$ pipenv run python app.py
立ち上げたローカルのWeb APIにコールをしてみましょう。Chainlinkノードへは'{"id": "0", "data": {}}'
といったjson形式のデータをリクエストする必要があります。以下の例では {"from": "ETH", "to": "USD"}
ですが、CryptoCompreのこのURLを叩くということに翻訳されて結果が返されます。External Adaptersではリターンに {"jobRunID": "0", "data": {}}
が少なくとも含まれなければいけません。この例でいうと data
jobRunID
および result
と statusCode
です。 result
と statusCode
は簡潔に結果を返すためおよびエラーハンドリングの目的で追加されています。必須のパラメータではありません。
$ curl -X POST -H "content-type:application/json" "http://localhost:8080/" --data '{"id": 0, "data": {"from": "ETH", "to": "USD"}}'{
"data": {
"USD": 601.71,
"result": 601.71
},
"jobRunID": 0,
"result": 601.71,
"statusCode": 200
}
ブログでは次にこのサンプルコードをOpenWeatherMapからお天気情報を取得するコードへ修正していく内容が始まります。認証情報およびお天気情報を取得したい街のクエリストリングを渡すことでWeb APIからの情報を上記の {"jobRunID": "0", "data": {}}
のフォーマットにラップして返すAPIへと作り変えます。つまりExternal AdaptersはChainlinkノード上へ乗せることができるAPIラッパー的なもののようです。前回自作のWeb APIを作っていますので、ここではサンプルのPythonコードのAPIを修正して自作したWeb APIをExternal Adaptersのコードでラップしてみます。その際にAWS API Gatewayへの認証情報も使用できるようにしていきます。
前回の自作Web APIでやってみる
この章は作業した順番にだらだら書いていきます。まずは自作したWeb APIへの認証機能の導入です。今回はAPI GatewayのAPIキーを使いました。API GatewayのAPIキーは本来認証の用途でなくアクセス制限を行うための機能です。しかしヘッダーに x-api-key: xxx
と指定するだけで簡単に認証もどきができあがるのでテスト用途にはもってこいです。良い子はCognitoとか使いましょう。
作成はAWSコンソールでぽちぽちしました。まず『Create API Key』でキーを作成してメソッドリクエストのAPI Key Requiredをtrueにします。この変更をステージへデプロイします。SAMでProdとStageの2つのステージが作成されていますが、今回はProd側だけに変更をデプロイしました。脳停止でできるぐらい簡単ですね。
APIの使用料プランを作成してRate/Burst/Quotaなどを設定した後に作成したAPI Keyを紐づけます。設定が反映されるとステータスを使用料プランのダッシュボードで確認可能です。今回はProdステージのみデプロイしてますので、ProdはAPI Keyの認証付き、StageはAPI Keyの認証なしでAPIが叩けるはずです。
$ curl -X GET -H "x-api-key:xxxxx" https://65a4j2qftl.execute-api.ap-northeast-1.amazonaws.com/Prod/test?key=bitcoin
{"Close": 19196.23, "sma": 19211.507499999996, "dis": 0.9992047734931787, "name": "bitcoin", "return": -0.00437796670649937}$ curl -X GET -H "Content-Type: application/json" https://65a4j2qftl.execute-api.ap-northeast-1.amazonaws.com/Stage/test?key=bitcoin
{"Close": 19196.23, "sma": 19211.507499999996, "dis": 0.9992047734931787, "name": "bitcoin", "return": -0.00437796670649937}
クローンしたサンプルコードへ変更を加えていきます。リポジトリはブログとは異なりPythonベースのものを使用しています。まずはAdapterクラスからです。 base_url
は自作のWeb APIのURLへ変更してください。また from
と to
という2つのパラメータがありますが今回の自作のWeb APIは key
しかとりません。自作したWeb APIの呼び出しに沿うようにコードを編集します。
base_url = 'https://min-api.cryptocompare.com/data/price'
from_params = ['base', 'from', 'coin']
to_params = ['quote', 'to', 'market']
認証情報の文字列はヘッダーへ渡す必要がありますがAPIリクエストはBridgeクラスの request
で実行されていることが分かりました。この箇所のヘッダーに x-api-key
を設定します。API Key 自体はos.environ.get
から取得、ブログのやり方と同様に環境変数からセットすることにしました。
修正したAdapterクラスはこちらです。
CHAINLINK_ENVという環境変数に事前にAPI Keyを設定してからAPPサーバを立ち上げます。テストしてみましょう。
$ export CHAINLINK_ENV=xxxxxx
$ echo $CHAINLINK_ENV # confirm the env is set properly$ pipenv run python app.py
ローカルのAPIサーバが立ち上がっているのでCryptoCompreの場合と同じようにCURLで叩けます。但し今回は自作のAPIを呼び出すので data
のところへ {"key": "bitcoin"}
を格納します。結果は成功です。{"jobRunID": "0", "data": {}}
という形式に result
statusCode
が追加されたjsonが取得できています。 result
はリターンの比率を設定するようにしてみました。Chainlinkノードはこれを理解して処理してくれるようです。意外と簡単ですね。
$ curl -X POST -H "content-type:application/json" "http://localhost:8080/" --data '{"id": 0, "data": {"key": "bitcoin"}}'{
"data": {
"Close": 19223.88,
"dis": 1.0005121255082223,
"name": "bitcoin",
"result": 0.001035722263966976,
"return": 0.001035722263966976,
"sma": 19214.039999999997
},
"jobRunID": 0,
"result": 0.001035722263966976,
"statusCode": 200
}
External Adaptersの導入方法
最後に作成したExternal Adaptersをどうやってネットワークへ導入するかですが、これには3つの方法があると書かれています。おそらく多くのノードで運用されているアダプターのほうが分散化されていて強固かつ信頼性が高いということだと思います。なので2.のノードオペレーターへアダプター追加を依頼するのが正解なんでしょうか。わけわからそですが。
個人的にはノード運用を行うのもおもしろいのではと思いました。
今回は作成したExternal Adaptersをホスティングする方法の調査までです。簡単ですが前回作成したWeb APIを使うExternal Adaptersのラッパーを作成してローカルマシン上でExternal Adaptersとして動作することが確認できました。External Adaptersを使用したスマートコントラクトについては以下の公式サイトが参考になります。[8]