Application

対象読者: アーキテクト、アプリケーションおよびスマートコントラクト開発者

アプリケーションは、台帳に対するトランザクションを送信したり台帳の内容のクエリを行うことで、ブロックチェーンネットワークとやりとりします。 このトピックでは、アプリケーションがこれを行う仕組みについて取り上げます。 ここでのシナリオにおいては、組織は、コマーシャルぺーパーのスマートコントラクトで定義された、発行購入現金化のトランザクションを実行するアプリケーションを用いてPaperNetにアクセスします。 MagnetoCorpのアプリケーションがコマーシャルペーパーを発行するアプリケーションは基本的なものですが、理解に必要な重要な点をすべて扱っています。

このトピックでは、以下の項目について取り上げます。

理解を助けるために、以降ではHyperledger Fabricで提供されているコマーシャルペーパーのサンプルアプリケーションを参照していきます。 このアプリケーションをダウンロードしてローカルで実行することができます。 アプリケーションは、JavaScriptとJavaの両方で書かれていますが、ロジックは言語にあまり依存していないので、何が起きているかを簡単に理解することができるでしょう。(Go版も提供される予定です)

Basic Flow

アプリケーションは、ブロックチェーンネットワークとFabric SDKを用いてやりとりを行います。 下記の図は、アプリケーションがコマーシャルペーパーのスマートコントラクトをどのように実行するかを単純化したものです。

develop.application PaperNetアプリケーションが、発行トランザクション要求を送信するために、コマーシャルペーパーのスマートコントラクトを実行します。

アプリケーションはトランザクションを送信するために、次の6つの基本的なステップに従わなければなりません。

  • ウォレットからアイデンティティを選択する
  • ゲートウェイに接続する
  • 目的のネットワークにアクセスする
  • スマートコントラクトに対するトランザクション要求を作成する
  • トランザクションをネットワークに送信する
  • 応答を処理する

これから、典型的なアプリケーションがFabric SDKを用いてこの6つのステップをどのように実行するかを見ていきます。 issue.jsに、このアプリケーションのコードが含まれています。 ブラウザで見るか、ダウンロードしているならお好きなエディタで開いて見てください。 アプリケーションの全体構造を確認してみてください。 コメントやスペースも含んでさえ、たったの100行のコードです!

Wallet

issue.jsの冒頭の方で、2つのFabricのクラスがスコープに取り込まれているのがわかります。

const { Wallets, Gateway } = require('fabric-network');

fabric-networkに含まれるクラスについては、Node SDKのドキュメントで参照することができますが、今のところは、MagnetoCorpのアプリケーションがPaperNetに接続するのに、どのようにこれらのクラスを用いているかを見ていきましょう。 アプリケーションは、FabricのWalletクラスを次のように使っています。

const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');

walletに対して、ローカルファイルシステムのウォレットがどのように指定されているかがわかるでしょうか。 ウォレットから取得されるアイデンティティは、明らかにIsabellaと呼ばれるユーザーのもので、このユーザーがissueアプリケーションを使用しています。 ウォレットは、アイデンティティのセット、すなわち複数のX.509デジタル証明書を保持し、PaperNetやそのほかのFabricネットワークにアクセスするために使うことができます。 もし、チュートリアルを動かしてこのディレクトリの中を見てみると、Isabellaに対するアイデンティティの資格情報があることがわかるでしょう。

ウォレットは、政府発行の身分証明書や運転免許証やATMカードのデジタル版を保持しているものと考えてください。 その中のX.509デジタル証明書は、それを保持しているものと組織を関連付けるもので、すなわち、ネットワークのチャネルへのアクセスする権利を与えているものということになります。 例えば、IsabellaはMagnetoCorpの管理者であるかもしれません。この場合、DigiBankのBalajiといったほかのユーザーとは異なる特権を与えられているかもしれません。 さらに、スマートコントラクトは、その処理の際に、トランザクション・コンテキストを用いて、このアイデンティティを取得することができます。

ウォレットは、いかなる形の現金やトークンも保持しておらず、アイデンティティを保持するものであることに注意してください。

Gateway

二つ目の重要なFabricのクラスは、Gatewayです。 もっとも重要なことは、ゲートウェイは、ネットワーク(ここでは、PaperNet)に対するアクセスを提供する、1つあるいは複数のピアを認識しているということです。 以下のようにして、issue.jsはゲートウェイに接続を行っています。

await gateway.connect(connectionProfile, connectionOptions);

gateway.connect()には、2つの重要なパラメータがあります。

  • connectionProfile: PaperNetへのゲートウェイとなるピアのセットの情報を含むコネクションプロファイルのファイルシステム上での位置
  • connectionOptions: issue.jsがPaperNetとのやりとりする際に用いられるオプション

クライアントアプリケーションがゲートウェイを使って、変化しうるネットワークトポロジーの影響を直接受けないようにしているのがわかるでしょうか。 ゲートウェイは、コネクションプロファイルオプションを用いて、トランザクション提案を適切なピアノードに送信する処理の面倒をみます。

コネクションプロファイル./gateway/connectionProfile.yaml を少し確認してみてください。 読むのが簡単なようにYAMLを用いています。

このファイルは、オブジェクトに変換されてロードされます。

let connectionProfile = yaml.safeLoad(file.readFileSync('./gateway/connectionProfile.yaml', 'utf8'));

今ここでは、下記に示す、channels:peers:セクションだけが重要なセクションです。(説明を簡単にするために多少改変してあります)

channels:
  papernet:
    peers:
      peer1.magnetocorp.com:
        endorsingPeer: true
        eventSource: true

      peer2.digibank.com:
        endorsingPeer: true
        eventSource: true

peers:
  peer1.magnetocorp.com:
    url: grpcs://localhost:7051
    grpcOptions:
      ssl-target-name-override: peer1.magnetocorp.com
      request-timeout: 120
    tlsCACerts:
      path: certificates/magnetocorp/magnetocorp.com-cert.pem

  peer2.digibank.com:
    url: grpcs://localhost:8051
    grpcOptions:
      ssl-target-name-override: peer1.digibank.com
    tlsCACerts:
      path: certificates/digibank/digibank.com-cert.pem

channel:でネットワークチャネルのPaperNet:と、その二つのピアが定義されているのがわかるでしょうか。 MagnetoCorpはpeer1.magenetocorp.comを持ち、DigiBankはpeer2.digibank.comを持ち、二つのピアともにエンドーシングピアのロールを持っています。

これらのピアは、ネットワークアドレスなどの接続のための方法などの詳細を含んでいるpeers:キーに関連付けられます。

コネクションプロファイルは、ピアだけでなく、ネットワークチャネル、Orderer、組織、CAなど、非常に多くの情報を含んでいますので、全部が理解できなくても心配する必要はありません!

では次に、connectionOptionsオブジェクトのほうを見ていきましょう。

let connectionOptions = {
    identity: userName,
    wallet: wallet,
    discovery: { enabled:true, asLocalhost: true }
};

ゲートウェイに接続するのに使う必要のある、userNameというアイデンティティ、walletというウォレットが、オプションで指定されているのがわかるでしょうか。 これらの変数は、この前のコードで値が代入されています。

このほかにも、コネクションオプションがあり、アプリケーションがSDKに対してその挙動をより細かく指示するのに使うことができます。

例:

let connectionOptions = {
  identity: userName,
  wallet: wallet,
  eventHandlerOptions: {
    commitTimeout: 100,
    strategy: EventStrategies.MSPID_SCOPE_ANYFORTX
  },
}

ここでは、commitTimeoutによって、トランザクションがコミットされたかどうかの結果を、100秒間まで待つようにSDKに指示しています。 そして、strategy: EventStrategies.MSPID_SCOPE_ANYFORTXは、MagnetoCorpの1つのピアがトランザクションを確認した時点で、SDKはアプリケーションに通知してよいということを表しています。 これに対して、strategy: EventStrategies.NETWORK_SCOPE_ALLFORTXでは、MagnetoCorpとDigiBankのすべてのピアがトランザクションを確認するまで待つ必要があります。

コネクションオプションによって、アプリケーションが目的に応じた挙動を、実現方法を気にせずに指定することができます。 もし必要であれば、詳細を確認することができます

Network channel

connectionProfile.yamlのゲートウェイで定義されたピアによって、issue.jsはPaperNetにアクセスできます。 これらのピアは、複数のネットワークチャネルに参加することができるので、アプリケーションはゲートウェイによって複数のネットワークチャネルへのアクセスを得ることができます。

アプリケーションが特定のチャネルをどのように選択するかを見てください。

const network = await gateway.getNetwork('PaperNet');

これ以降では、networkはPaperNetへのアクセスを提供します。 さらに、もしアプリケーションが他のネットワークBondNetへのアクセスが同時に必要であったなら、下記のように簡単にできます。

const network2 = await gateway.getNetwork('BondNet');

これでアプリケーションは、二つ目のネットワークBondNetへのアクセスが、PaperNetと同時に可能になります!

ここに、Hyperledger Fabricの強力な機能を見ることができます。 すなわち、それぞれが複数ネットワークチャネルに参加している、複数のゲートウェイのピアに接続することで、アプリケーションはネットワークのネットワークに参加することができる、ということです。 アプリケーションは、gateway.connect()で提供したウォレットのアイデンティティに応じて、それぞれのチャネルで異なる権利を有します。

Construct request

さて、アプリケーションはコマーシャルペーパーを発行することができるようになりました。 これを行うには、CommercialPaperContractを使うことになりますが、スマートコントラクトへのアクセスもまた非常に簡単です。

const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');

アプリケーションがpapercontractという名前と、明示的にコントラクトの名前org.papernet.commercialpaperを指定していることに注目してください。 複数のコントラクトを含むチェーンコード papercontract.jsから、1つのコントラクトを、コントラクト名によって選んでいることがわかるでしょうか。 PaperNetでは、papercontract.jsが、papercontractという名前でチャネルにインストールされデプロイされていました。 もし興味があれば、複数のスマートコントラクトを含むチェーンコードをどのようにデプロイするのかのドキュメントを参照してください。

もしアプリケーションが同時に、PaperNetやBondNetの他のコントラクトへのアクセスが必要だったなら、これも下記のように簡単に行えるでしょう。

const euroContract = await network.getContract('EuroCommercialPaperContract');

const bondContract = await network2.getContract('BondContract');

この例では、コントラクト名を使用していないことに注意してください。 これは、ファイルあたり1つのスマートコントラクトしかないからで、getContract()は最初にみつけたコントラクトを使用します。

最初のコマーシャルペーパーを発行するのに、MagnetoCorpが下記のようなトランザクションを用いたことを思い出してください。

Txn = issue
Issuer = MagnetoCorp
Paper = 00001
Issue time = 31 May 2020 09:00:00 EST
Maturity date = 30 November 2020
Face value = 5M USD

それでは、このトランザクションをPaperNetに送信しましょう!

Submit transaction

トランザクションの送信は、下記の通り、SDKの1つのメソッドの呼び出しで行えます。

const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');

submitTransaction()のパラメータが、トランザクション要求のそれに一致しているのがわかります。 この値が、スマートコントラクトのissue()メソッドに渡され、新しいコマーシャルペーパーを作成するのに使用されます。 このメソッドのシグネチャを思い出してください。

async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {...}

アプリケーションが、submitTransaction()を発行すると、すぐにスマートコントラクトに制御が移るように見えるかもしれませんが、それは違います。 SDKが裏では、connectionOptionsconnectionProfileの詳細を使用して、必要なエンドースメントを得られるネットワークの適切なピア群にトランザクション提案を送信しています。 しかし、アプリケーションはこういったことを気にする必要はなく、ただsubmitTransactionを呼び出すだけで、SDKが後の面倒は見てくれます!

submitTransaction APIには、トランザクションのコミットを待つというプロセスが含まれていることに注目してください。 コミットを待つことは必要不可欠で、もし、これがなければ、トランザクションの順序付け、検証、台帳へのコミットが成功したかどうかを知ることができないでしょう。

次は、アプリケーションが応答をどのように処理するかを見ていきましょう!

Process response

papercontract.jsで、下記のように、issueトランザクションがコマーシャルペーパーの応答を返していたのを思い出しましょう。

return paper.toBuffer();

ここでちょっとしたクセがあることに気づくでしょう。 新しいpaperはbufferに変換してから、アプリケーションに返さなければならないということです。 issue.jsでは、クラスメソッドCommercialPaper.fromBuffer()を用いて、応答のbufferからコマーシャルペーパーに復元しているのに注目してください。

let paper = CommercialPaper.fromBuffer(issueResponse);

これによって、下記のように、完了メッセージの説明文でpaperを自然な形で使うことができます。

console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully issued for value ${paper.faceValue}`);

アプリケーションとスマートコントラクトで、同じpaperクラスが使われていることがわかります。 コードの構造をこのようにすることで、非常に読みやすく、また再利用しやすくすることができます。

トランザクション提案の場合と同様に、スマートコントラクトが完了するとすぐにアプリケーションに制御が移るように見えるかもしれませんが、これも違います。 SDKが、裏で合意形成プロセス全体を処理し、connectionOptionsのstrategyにしたがって、完了した時点でアプリケーションに通知します。 もし、SDKが実際に裏で何をしているかに興味ある場合には、詳細なトランザクションのフローを参照してください。

以上です! このトピックで、MagnetoCorpのアプリケーションがPaperNetで新しいコマーシャルペーパーを発行するやり方を通じて、サンプルアプリケーションからスマートコントラクトをどのように呼ぶかが理解できたでしょう。 アーキテクチャのトピックで、これらの背後にある、重要な台帳やスマートコントラクトのデータ構造を見てみましょう。