Amanzon Manged Blockchain 〜ブロックをモニタリングできるChannelEventHubの紹介〜

by 仲尾 和洋

はじめに

この記事は、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がトランザクションをまとめた情報

EventOverview

なお、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通りが存在します。

ただし、上記で使用する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年ほどで切れてしまう問題もあります。

Contact

お問い合わせ

Contact