Amanzon Manged Blockchain 〜ブロックをモニタリングできるChannelEventHubの紹介〜
はじめに
この記事は、Blockchain Advent Calendar 2019の10日目の記事です。
ブロックチェーンスタジオでは、AdFraudの情報を共有するプラットフォームの構築に、Amazon Managed Blockchain上で動作しているHyperledger Fabricを利用する予定です。本記事では、このHyperleger Fabricのブロックをモニタリングとして利用できるEventサービスについて、仕様とサンプルプログラムと共に紹介したいと思います。
なお、Hyperledger FabricのLTSバージョンは1.4ですが、Amazon Managed Blockchainは1.2を利用しているため、1.2の仕様で説明したいと思います。
Hyperledger Fabricの仕様
Eventサービスについて
Eventサービスは、Peerが下記の情報を取得した際、クライアントアプリケーションが随時情報を取得できる仕組みになっています。
- ChaincodeEvent: Chaincodeが処理の途中でトランザクションに刻み込む情報
- トランザクション: Endorsement Peerが検証したInvokeの結果
- ブロック: Ordererがトランザクションをまとめた情報
なお、Hyperledger Fabricは、チャンネルごとに分かれてブロックを生成します。このため、EventサービスでHyperledger Fabric上のEventを全て監視する場合は、それぞれのチャンネルごとにEventサービスの設定を行う必要があります。
Node SDKの仕様
注意: Hyperledger Fabric v1.4を利用してクライアントをNode SDKで作成する場合、本記事の内容を参考にするより、fabric-network.Networkクラスの利用を検討してください。利用方法については、チュートリアルのfabric-network: How to listen to eventsに記載があります。
ChannelEventHubクラスのオブジェクト化
EventサービスをNode SDKを用いて使用する場合、ChannelEventHubクラスを用いることで、Eventサービスを利用することができます。なお、このChannelEventHubクラスのオブジェクト化には、下記の3通りが存在します。
- new ChannelEventHub(channel, peer)
- Channel.newChannelEventHub(peer)
- Channel.getChannelEventHubsForOrg(mspid)
ただし、上記で使用するchannelオブジェクトを生成する際、Clientオブジェクトにて、MSPに所属するユーザーのUserオブジェクトを事前に取得・設定しておく必要がある。
Peerとの接続設定
ChannelEventHubを用いてPeerからEventの情報を取得するには、ChannelEventHubオブジェクトを利用してPeerに接続する必要があります。この際、ChannelEventHub.connect(full_block)
を利用します。なお、引数のfull_block
は、booleanを指定します。
このfull_block
の設定により、PeerからEventの情報を取得する際、情報全てを取得するか、一部の情報のみ取得するか変更されます。例えば、ブロック情報を取得する場合、full_block
がfalseだと、下記のようなトランザクションの一覧しか取得できません。
{
"channel_id": "transform",
"number": "49",
"filtered_transactions": [
{
"Data": "transaction_actions",
"txid": "0aa86832525d70ce3ea8858ed596a7b6f3ab35e0f4e000871a76547c741ff7e5",
"type": "ENDORSER_TRANSACTION",
"tx_validation_code": "VALID",
"transaction_actions": {
"chaincode_actions": []
}
},
{
"Data": "transaction_actions",
"txid": "4a64be65fa2d27e2d360e4eeef0f801b679a3aea7b5aeed113a0b1f9dcbf57f2",
"type": "ENDORSER_TRANSACTION",
"tx_validation_code": "VALID",
"transaction_actions": {
"chaincode_actions": []
}
}
]
}
ChaincodeEventの取得
PeerからChaincode Eventの情報を取得するには、クライアントアプリケーションでChannelEventHub.registerChaincodeEvent(ccid, eventname, onEvent, onError, options)
を利用します。なお、各引数の説明は下記になります。
名前 | 型 | 説明 |
---|---|---|
ccid | string | ChaincodeEventを取得するChaincodeのIDを指定する。 |
eventname | string | Chaincode内でChaincodeStub.SetEventで指定したイベント名を指定する。 |
onEvent | function | ChaincodeEventを取得するたびに実行する関数。ChaincodeEventの情報、およびChaincodeEventが発行されたブロック番号、トランザクションID、実行結果が引数として渡される。(参考) |
onError | function | ブロックを取得する際にエラーが発生した場合に実行する関数。エラーオブジェクトが引数になる。 |
options | RegistrationOpts(object) | ブロック取得時の設定 |
また、RegistrationOptsクラスのフィールドは、下記の通りです。
フィールド名 | 型 | 説明 |
---|---|---|
startBlock | integer | 取得するブロックの開始位置。現在の先頭ブロックよりも小さい数字である必要がある。 設定がない場合は、現在の先頭のブロックから取得する設定となる。 |
endBlock | integer | ブロックの取得を終了する数字。現在の先頭ブロックよりも大きい必要がある。 なお、この設定で指定したブロックを取得すると、onEventを実行しない設定となる。 |
unregister | boolean | ChannelEventHub.unregister()実行時に、Peerへの接続を自動的に削除する設定。 endBlockが設定されている場合はtrue、設定されていない場合はfalseにしないとエラーが発生する。 |
disconnect | boolean | ChannelEventHub.disconnect()実行時に、Peerからの接続を自動的に切断する設定。 endBlockが設定されている場合はtrue、設定されていない場合はfalseにしないとエラーが発生する。 |
トランザクションの取得
Peerからトランザクションの情報を取得するには、クライアントアプリケーションでChannelEventHub.registerTxEvent(txid, onEvent, onError, options)
を利用します。なお、各引数の説明は下記になります。
名前 | 型 | 説明 |
---|---|---|
txid | string | 取得するトランザクションのID |
onEvent | function | トランザクションを取得するたびに実行する関数。取得したトランザクションのID、実行結果、ブロックの高さが引数として渡される(参考)。 |
onError | function | トランザクションを取得する際にエラーが発生した場合に実行する関数。エラーオブジェクトが引数になる。 |
options | RegistrationOpts(object) | トランザクション取得時の設定 |
RegistrationOptsオブジェクトの各フィールドの説明については、ChaincodeEventでの説明と同様になります。
ブロックの取得
PeerがOrdererからブロックを取得するたびに、クライアントアプリケーションでブロック情報を受け取る設定するにはChannelEventHub.registerBlockEvent(onEvent, onError, options)
を利用します。なお、各引数の説明は下記になります。
名前 | 型 | 説明 |
---|---|---|
onEvent | function | ブロックを取得するたびに実行する関数。取得したブロック情報が引数になる。 |
onError | function | ブロックを取得する際にエラーが発生した場合に実行する関数。エラーオブジェクトが引数になる。 |
options | RegistrationOpts(object) | ブロック取得時の設定 |
RegistrationOptsオブジェクトの各フィールドの説明については、ChaincodeEventでの説明と同様になります。
サンプルコード
ChaincodeEventHubを用いて、ブロックをモニタリングして標準出力に表示するサンプルコードは下記になります。なお、process.env.USER_ARTIFACT_PATH
には、管理者ユーザーの秘密鍵と証明書が配置しています。
const Client = require('fabric-client');
const mspid = process.env.MSP_ID;
const channelId = process.env.CHANNEL_ID;
const peerAddress = process.env.PEER_ADDRESS;
const ordererAddress = process.env.ORDERER_ADDRESS;
const initializeClient = async () => {
const client = new Client();
const kvs = await Client.newDefaultKeyValueStore({ path: '/tmp' });
client.setStateStore(kvs);
const cryptoSuite = Client.newCryptoSuite();
const cryptoKeyStore = Client.newCryptoKeyStore({ path: '/tmp' });
cryptoSuite.setCryptoKeyStore(cryptoKeyStore);
client.setCryptoSuite(cryptoSuite);
return client;
}
const setAdminUserContext = async (client) => {
const cryptoArtifactsPath = process.env.USER_ARTIFACT_PATH;
const privateKeyPath = `${cryptoArtifactsPath}/keystore/admin-private.pem`;
const signedCertPath = `${cryptoArtifactsPath}/signcerts/cert.pem`;
const privateKeyPEM = fs.readFileSync(privateKeyPath);
const signedCertPEM = fs.readFileSync(signedCertPath);
const params = {
username: 'admin',
mspid,
cryptoContent: {
privateKeyPEM,
signedCertPEM,
},
skipPersistence: false,
}
await client.createUser(params);
}
const initializeChannel = (client) => {
const channel = client.newChannel(channelId);
channel.addPeer(client.newPeer(peerAddress));
channel.addOrderer(client.newOrderer(ordererAddress));
return channel;
}
const main = async () => {
const client = await initializeClient();
await setAdminUserContext(client);
const channel = initializeChannel(client);
const channelEventHubArray = channel.getChannelEventHubsForOrg(mspid);
channelEventHubArray[0].registerBlockEvent(
(block) => console.log(block),
(err) => channelEventHubArray.disconnect(),
)
channelEventHubArray[0].connect(true);
}
main();
おわりに
今回は、Hyperledger FabricのEventサービスと、その利用方法について紹介しました。Hyperledger Fabricのクライアント側のNode SDKについては、v1.4を境に大幅な変更があるため、公式ページのTutorialsやサンプルリポジトリのfabcarを参考に実装する方が望ましいです。
また、Hyperledger TokyoのようなMeetupに参加していると、ライブラリの実装は、旧Hyperledger Composerの開発メンバーが主導してコントリビュートを行うため、Node.jsが最初で、その後にJava等で実装する方針と聞いてます。このため、今後は、実装時期や基盤のバージョンに応じてライブラリを随時検討し直し、アップデートをかける必要があります(現状、LTSも1年ほどで切れてしまう問題もあります。
Author