この記事は株式会社 Ginco のテックブログとして書いています。
この記事では、Polkadot のノード運営やウォレットアプリケーションを運用するにあたって、非常に重要なモニタリング項目の一つである「Runtime Upgrade のモニタリング」をどのように行う方法があるかについて、検証・紹介していこうと思います。
Runtime とは
Runtime とは、Polkadot と Substrate におけるブロックチェーンのロジックや機能を定義するプログラムのことで、Runtime は WebAssembly (WASM)というバイナリ形式でコンパイルされ、ブロックチェーンノード上の仮想マシンで実行されます。
引用: https://docs.substrate.io/fundamentals/architecture/#high-level-overview
Runtime には、トランザクションの検証方法、アカウントの管理方法、コンセンサスの仕組みなどが含まれており、各機能は Pallet という単位でモジュール化されており、必要な機能を選択して組み合わせることができます。
Runtime Upgrade とは
Polkadot(Substrate)以外のブロックチェーンの多くは、コアロジックの修正の際にフォーク(ハードフォークやソフトフォーク)を必要とし、新しい変更に対応したノードソフトウェアがフォークのたびにリリースされます。
それにより、後方互換性の無い変更が加えられた場合に、その変更を含まないノードは、他のノードとの合意を維持できなくなるために、ハードフォークが発生します。ハードフォークはそのアップグレードの性質により、ガバナンス的にもロジック的にも厄介な問題が発生する可能性があります。
一方、Polkadot(Substrate)に導入されている Forkless Upgrade はオンチェーン上にコアロジックや機能を定義した WASM コードを配信することにより、数千のノードオペレーターにフォークのたびにアップグレードを要求する調整の問題を解決しています。
また、Runtime Upgrade は Polkadot のステークホルダーが、オンチェーンのガバナンスシステムを介してアップグレードを提案し承認されると、自律的に実行されます。
Runtime Upgrade のモニタリング
Runtime Upgrade は、ブロックチェーンの機能性やセキュリティに直接関係する更新であり、それらのアップグレードが正常に機能することを確認する上で、日常的にモニタリングを行うことが非常に重要となってきます。
まず、Polkadot や Substrate のエコシステムは非常に活発であり、新しいプロトコルや機能が継続的に導入されています。最新のアップグレードに関する情報を把握することで、新しい機能やプロトコルについての理解を深め、それらを活用することができたり、新しい機能によってアプリケーションの改修が必要となる場合に、早めのアクションを取ることができるようになります。
さらに、アップグレードには、既存のノードに対して互換性がない場合があるため(直近でいうと WeightV2 の導入で一部 API に後方互換性が無くなっていた)、アップグレードに関する情報を把握し、必要な手順を踏んでアップグレードを実行することで、ノードの更新や運用を円滑に行うことができます。
以上のような理由から、Polkadot や Substrate の Runtime Upgrade をモニタリングすることは、非常に重要であるといえます。
では次項から、具体的な Runtime Version(Spec Version)の取得方法および Runtime Upgrade のモニタリング方法を紹介していきます。 なお、サンプルコードは javascript を使用します。
Runtime Upgrade をモニタリングする
以下の方法を使用して、RuntimeVersion を定期的にモニタリングすることによって、Runtime Upgrade がいつ発生したかをモニタリングすることが可能です。
方法 1: RPC「state.getRuntimeVersion」で specVersion を取得する
まず 1 つ目は RPC メソッドの state.getRuntimeVersion
を使用して Spec Version(Runtime Version)を取得する方法です。
curl を使用する場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
curl --request POST \ --url https://rpc.polkadot.io \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "method": "state_getRuntimeVersion", "params": [], "id": 1 }' ##### Return ##### { "jsonrpc": "2.0", "result": { "specName": "polkadot", "implName": "parity-polkadot", "authoringVersion": 0, "specVersion": 9370, "implVersion": 0, "apis": [...], "transactionVersion": 20, "stateVersion": 0 }, "id": 1 } ##################
polkadot-js(javascript)を使用する場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Import the API const { ApiPromise, WsProvider } = require("@polkadot/api"); async function main() { // Initialise the provider to connect to the local node const provider = new WsProvider("wss://rpc.polkadot.io"); // Create our API with a default connection to the local node const api = await ApiPromise.create({ provider }); const runtimeVersion = await api.rpc.state.getRuntimeVersion(); console.log(JSON.stringify(runtimeVersion)); } main() .catch(console.error) .finally(() => process.exit()); // Return: {"specName":"polkadot","implName":"parity-polkadot","authoringVersion":0,"specVersion":9370,"implVersion":0,"apis":[...],"transactionVersion":20}
方法 2: Storage の「system.lastRuntimeUpgrade」から specVersion を取得する
:::message Polkadot や Substrate における Storage は、ブロックチェーンの状態を永続化するためのデータストアで、トランザクションによって変更される変数や値を格納するために使用され、トライと呼ばれるデータ構造で実装されています。 :::
2 つ目は RPC メソッドの state.getRuntimeVersion
を使用して Storage から Spec Version(Runtime Version)を取得する方法です。
polkadot-js(javascript)を使用する場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Import the API const { ApiPromise, WsProvider } = require("@polkadot/api"); async function main() { // Initialise the provider to connect to the local node const provider = new WsProvider("wss://rpc.polkadot.io"); // Create our API with a default connection to the local node const api = await ApiPromise.create({ provider }); const lastRuntimeUpgrade = await api.query.system.lastRuntimeUpgrade(); console.log(lastRuntimeUpgrade); } main() .catch(console.error) .finally(() => process.exit()); // Return: {"specVersion":9370,"specName":"polkadot"}
方法 3: websocket を用いて「chain_subscribeRuntimeVersion」を subscribe する
3 つ目は Websocket のエンドポイントに対して、RPC メソッドの chain_subscribeRuntimeVersion
を使用して Spec Version(Runtime Version)をサブスクライブする方法です。
こちらの方法では、Runtime Upgrade が実行されるごとに、state_runtimeVersion
のレスポンスが流れてきます。
- wscat を使用する場合
|
|
事前に Runtime Upgrade のタイミングを知る
前項では、RuntimeVersion を定期的にモニタリングすることによって、Runtime Upgrade がいつ発生したかをモニタリングする方法を紹介しました。 しかし、アップグレードされる Runtime の内容によっては、事前にアップグレード内容を把握し、アプリケーションの修正やノードのアップデートを行う必要があるかもしれません。 前項の方法では事前にいつ Runtime が Upgrade されるかは把握できないので、今回はブロックチェーン上の投票(democracy)情報を取得することにより、いつ Runtime Upgrade が実施されるかを知る方法を紹介します。
ブロック内の democracy イベントを取得し、Referendum(投票)の ID を取得
今回は例として、ReferendumIndex: 107
の投票が始まったブロックの情報を取得します。
該当ブロックのリンクはこちら: https://polkadot.subscan.io/block/14442718
|
|
これで、14442718
ブロックにおいて、ReferendumIndex 107
の投票が始まったことが確認できます。
このように、ブロックごとに Event を取得して、democracy のイベントを監視する必要があります。
投票イベントの詳細を確認
Referendum(投票)が開始されるのは、何も Runtime Upgrade の時だけではありません。 よって、この Referendum で実行されようとしているのは何なのかを取得する必要があります。
|
|
:::details 取得した投票情報
- 取得した投票情報
- 投票開始(ブロック高: 14442718)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{ "ongoing": { "end": 14543518, "proposal": { "lookup": { "hash": "0x56ee5c8542848bfa5c8f5be09ebf74857984031841b50679f2d5009ecfb41f6e", "len": 1328944 } }, "threshold": "SimpleMajority", "delay": 400, "tally": { "ayes": 0, "nays": 0, "turnout": 0 } } }
- 投票締め切り直前(ブロック高: 14543517)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
{ "ongoing": { "end": 14543518, "proposal": { "lookup": { "hash": "0x56ee5c8542848bfa5c8f5be09ebf74857984031841b50679f2d5009ecfb41f6e", "len": 1328944 } }, "threshold": "SimpleMajority", "delay": 400, "tally": { "ayes": "0x00000000000000000016b45bc7ceb96d", "nays": 6463558200000, "turnout": "0x0000000000000000001bf6ba3dede5b7" } } }
- 投票締め切り(ブロック高: 14543518)
1
{ "finished": { "approved": true, "end": 14543518 } }
- 投票開始(ブロック高: 14442718)
:::
ReferendumInfo で取得できる情報には、以下のような情報が含まれています。
- end
- 投票終了のブロック高
- proposal
- 投票内容
- 投票可決時に実行される Extrinsic が PreImage という形で Storage に格納される
- threshold
- 投票方式
- 単純な多数決など様々な投票方法が存在
- 詳しくはこちらを参照(https://wiki.polkadot.network/docs/learn-governance#tallying)
- 投票方式
- delay
- 投票終了から投票内容が有効化されるまでの執行猶予ブロック数
- 今回の場合だと、投票が可決された場合に proposal が
end + delay (14543518 + 400) = 14543918
に有効化される
Preimage の内容を取得
前述したように、Referendum には、投票可決時に実行される内容が Preimage(Extrinsic)として含まれています。 その Preimage の内容によって Runtime が Upgrade されたりなどのチェーンへの変更が適用されます。
|
|
結果、今回の Referendum が可決されると、utility.withWeight
が実行されることになっていることが確認できます。
またutility.withWeight
はargs
に渡された Call を実行する関数なので、system.setCode
、つまり Runtime Upragde を行う Referendum だと確認することができました。
|
|
今回取得した Referendum:107 の情報まとめ
- 投票はブロック高:14442718 から始まり、ブロック高:14543518 で締め切られる
- 投票が可決された場合は、投票終了ブロック高から 400 ブロック経過(Delay)したタイミングで Preimage が実行
- Preimage の内容は
utility.withWeight
で Wrap されたsystem.setCode
が実行されるというもの
まとめ
今回は、Polkadot(Substrate)での Runtime Upgrade をモニタリングする方法について、紹介しました。
Polkadot(Substrate)以外のブロックチェーンの多くは、コアロジックの修正の際にフォーク(ハードフォークやソフトフォーク)を必要とし、ノードソフトウェアのリリース時にフォークタイミングがハードコードされることが多いので、フォークのタイミングを把握することが容易ですが、Polkadot(Substrate)のようなフォークレスアップグレードではブロックチェーン上の投票次第でコアロジックが修正されるので、ブロックチェーン上のアクティビティを注意深く監視しておく必要があります。
Polkadot(Substrate)を用いたアプリケーションを運用している方やノードを運用している方にとって、当記事で紹介した Runtime Upgrade のモニタリング方法が少しでも参考になれば幸いです。
感想やコードや内容についてのご指摘やアドバイスなどは Twitter(@nandehu0323)にいただけると非常にありがたいです!
株式会社 Ginco ではブロックチェーンを学びたい方、ウォレットについて詳しくなりたい方を募集していますので下記リンクから是非ご応募ください。
参考文献
https://docs.substrate.io/fundamentals/architecture/
https://wiki.polkadot.network/docs/build-protocol-info#runtime-upgrades
https://wiki.polkadot.network/docs/learn-runtime-upgrades#client-releases