はじめに

こんにちは。ブロックチェーン推進室の鈴木です。

私の所属するブロックチェーン推進室では、企業間取引に特化したエンタープライズブロックチェーンプラットフォーム:Cordaに関して機能調査を行っております。
Cordaは世界で350社を超える金融機関、規制当局、中央銀行、業界団体、システム・インテグレーターやソフトウェアベンダーにより構成されるR3エコシステムから、エンドユーザー目線で設計・開発されています。
また、4半期に1度のバージョンアップにより機能追加がされていますが、その機能がお客様に活用できるものなのか、ドキュメントだけではわからないことも多く、ブロックチェーン推進室独自で調査を進めています。

このブログでは、そういったCordaの機能調査をした結果を機能の使い方含めて紹介していきます。

Cordaの導入方法についてはこちらのドキュメントをご参照ください。

今後以下の内容で紹介しようと思います。

  1. Corda JMeterの使い方

  2. Corda Enterprise Performance検証 ノードスペックを変化することによる性能変化

  3. Corda Tokens SDK and Accounts

  4. Corda Enterprise Collaborative Recovery

  5. Corda Enterprise Archive Service

  6. Corda Firewallの設定方法

  7. Corda re-issuance Tokens SDK and Accountsを利用した一括「re-issuance」

  8. Corda Time-windows

  9. Corda Oracle

  10. Corda attachment

  11. Corda Webアプリケーション開発について(←今回はこちらの記事)

  12. Cordaを用いたNonFungibleTokenの実装

  13. CordaのTokens SDKでNFTマーケットプレイスを作ってみた

(以降、バージョンアップの都度、新機能を紹介予定)

第11回目は「Corda Webアプリケーション開発について」について紹介します。

はじめに

今回の記事では、CordaのWebアプリケーション開発について紹介します。
CorDappの開発に関する資料は多くありますが、フロント実装についてはあまり見受けられないように思います。
したがって本記事では、spring bootを使ったCordaのSampleのプロジェクトを元に独自でWebアプリケーションを開発いたしましたので、
そのWebアプリケーションの紹介とフロントとCorDappをRPCで接続する方法について紹介します。

Webアプリケーションの構成

▼構成図

CordaのWebアプリケーションの作成は以下の図の赤枠の範囲になります。
ClientSoftwareはHttpRequestができる画面であればどのような形でも問題ないので本記事ではWebAPIにフォーカスして解説します。
image-20220309172623025

図1:全体像


▼sampleプロジェクト

Cordaのsampleにあるものを参考にして作成します。
https://github.com/corda/samples-kotlin/tree/master/Basic/cordapp-example
したがって今回はspring bootを使った構成で紹介します。
フロントに関するディレクトリはclient配下のものすべてになります。
以下はsampleのclient配下の構成図になります。

◎ディレクトリ階層

├─clients
│ ├─build.gradle
│ │
│ └─src
│  └─main
│   ├─kotlin
│   │ └─net
│   │  └─corda
│   │    └─samples
│   │     └─example
│   │      └─server
│   │        ├─Controller.kt
│   │        ├─NodeRPCConnection.kt
│   │        └─Server.kt
│   └─resources
│    └─static
│     ├─ app.js
│     └─ index.html //表示するHTMLの画面

RPCClientについて

RPCとは

RPCとはRemote Procedure Call(リモートプロシージャコール)の略で遠隔手続き呼び出しのことを指します。
ネットワーク上の別のサーバのプログラムを呼び出し実行させる手法です。

 

RPCClient

RPCClientはRPCを実行するクライアントであり、指定したサーバに接続し、様々なタスクを実行できます。
つまり、RPCClientを使ってCordaノードのFlowを実行したり、vaultを参照したりすることができます。
CordaではRPCClientは、JVM互換の言語で作成する必要がありますが、Cordaには標準で以下の2種類のRPCClientが用意されています。
そのため自分で作成する必要はありません。

 

◎CordaRPCClient

「CordaRPCOps」リモートインターフェースを介してノードと対話をする場合に使用します。
このリモートインターフェースを通して、vaultからstateを抽出したり、Flowを実行するなどのRPC操作ができます。
他のRPC操作については以下を参照してください。

https://docs.r3.com/en/platform/corda/4.8/open-source/api-rpc.html

 

◎MultiRPCClient

Corda4.6から新しく追加されたモジュールの一つで、従来の「CordaRPCOps」を含む、Cordaが提供する他のリモートインターフェースを使用してノードと対話する場合に使用します。
MultiRPCClientとリモートインターフェースは1対1の関係なので使用するリモートインターフェースの数だけ、MultiRPCClientを作成する必要があります。

2022年3月現在、MultiRPCClientが利用できるリモートインターフェースは以下の通りです。

  • CordaRPCOps
    vaultからstateの抽出やFlowの実行などの基本的なRPC操作ができます。
  • AuditDataRPCOps
    RPCアクティビティのログを監査します。
  • FlowRPCOps
    FlowHospitalのFlowを再試行したり、Flowを停止したりできます。
  • NodeFlowStatusRpcOps
    FlowHospitalによって現在監視されているFlowのステータスを表示します。
  • NodeHealthCheckRpcOps
    ノードの死活監視の他、メモリ使用量などに関するレポートを取得できます。
  • NotaryQueryRpcOps
    特定のStateがすでに使用されているかNotaryに確認します。

注意点として「CordaRPCOps」以外のリモートインターフェースはCorda Enterprise版の機能のためOSSでは使用できません。

本記事ではCordaRPCClientでの実装を紹介しています。

設定手順

今回、WebアプリケーションとCorDappを作成したので起動から画面表示まで紹介します。
ここではフロントアプリからCorDappに接続する際の設定方法を紹介します。
※独自で作成したアプリのため、gradleタスクやRPCのポートなど上記のsampleプロジェクトとは一部異なります。

 

▼build.gradleの設定

build.gradle(一部省略)

dependencies {
    // Corda dependencies.
    compile "$corda_release_group:corda-rpc:$corda_release_version"
    compile "net.corda:corda-jackson:$corda_release_version"

    // CorDapp dependencies.
    compile project(":contracts")
    compile project(":workflows")
    
     // Spring boot.
    compile("org.springframework.boot:spring-boot-starter-websocket:$spring_boot_version") {
        exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
    }
    compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
    compile "org.apache.logging.log4j:log4j-web:${log4j_version}"
    compile "org.slf4j:jul-to-slf4j:$slf4j_version"
}

springBoot {
    mainClassName = "net.corda.samples.example.webserver.ServerKt"
}

/* This task will start the springboot server that connects to your node (via RPC connection). All of the http requests
 * are in the Controller file. You can leave the Server.kt and NodeRPCConnection.kt file untouched for your use.
 */
task runTemplateServerA(type: JavaExec, dependsOn: assemble) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'net.corda.samples.example.webserver.ServerKt'
    args '--server.port=10050', 
      '--config.rpc.host=localhost', 
         '--config.rpc.port=10006', 
         '--config.rpc.username=user1', 
         '--config.rpc.password=test'
}
  • dependencies
    ここで以下の依存関係を記述します。

    • CordaのRPCのモジュール
    • jacksonのモジュール
    • 対象のCorDapp
    • spring boot
    • log4j
  • task runTemplateServerA
    ここではWebサーバの起動タスクを設定します。
    Webアプリケーションを起動するためのメインクラスとRPCClientに渡すパラメータを記述します。
    gradlew runTemplateServerAを実行することで一つのノードのWebサーバが起動します。
    そのためノードごとにWebサーバを立てる必要があります。
    image-20220302174140654
    図2:ノードとクライアントの関係図

▼node.confの設定

node.conf(一部省略)

rpcSettings {
    address="localhost:10006"
    adminAddress="localhost:10007"
}
security {
    authService {
        dataSource {
            type=INMEMORY
            users=[
                {
                    user=user1                
                    password=test
                    permissions=[
                        ALL //全てのRPC操作を許可
                    ]                   
                }
            ]
        }
    }
}
  • rpcSetting
    ここではRPCのサーバとバインディングするホストとポートを設定します。
  • security
    RPC操作でのアクセスの設定を行います。
    usersの中でユーザ名やパスワード、さらにその中のpermissionsにどのRPC操作を許可するかなどの設定をします。
    ▽RPC操作の許可設定

    • 全てのRPC操作を許可
      (例:ALL)
    • 特定のFlowのみの実行許可
      StartFlow.という構文で記述します。
      (例:”StartFlow.net.corda.flows.ExampleFlow1″)
    • 特定のRPC操作のみの実行許可
      InvokeRpc.という構文で記述します。
      (例:”InvokeRpc.nodeInfo”)

実装方法

実装方法ではフロントアプリについて、主要な点を紹介します。
CorDppsの実装については割愛いたしますが概要のみ紹介します。

▼CorDapp

以下の画像は独自で作成したCorDappの全体像になります。
image-20220303135201759

図3:作成したCorDappの全体像

Token SDKとアカウントライブラリを使ったアカウント間でトークンのやり取りを行うCorDappです。
◎登場人物

  • BankA・・・ノード1
    • Alice・・・BankAに属するアカウント1
    • Bob・・・BankAに属するアカウント2
  • BankB・・・ノード2
    • Carol・・・BankBに属するアカウント1
    • Dave・・・BankBに属するアカウント2

▼フロントアプリ

NodeRPCConnection.kt

RPCClientの初期化を行います。
起動時に受け取ったパラメータを使ってRPCClientを生成し、ノードと接続します。
起動時のパラメータは、上記のbuild.gradleに記載した「runTemplateServerA」の値を参照します。
このパラメータとnode.confの「rpcSettingsに記述のaddress」と「securityに記述のusersのユーザ名とパスワード」は一致させる必要があります。

private const val CORDA_USER_NAME = "config.rpc.username"
private const val CORDA_USER_PASSWORD = "config.rpc.password"
private const val CORDA_NODE_HOST = "config.rpc.host"
private const val CORDA_RPC_PORT = "config.rpc.port"

@Component
open class NodeRPCConnection(
    @Value("${$CORDA_NODE_HOST}") private val host: String,
    @Value("${$CORDA_USER_NAME}") private val username: String,
    @Value("${$CORDA_USER_PASSWORD}") private val password: String,
    @Value("${$CORDA_RPC_PORT}") private val rpcPort: Int): AutoCloseable {

    lateinit var rpcConnection: CordaRPCConnection
        private set
    lateinit var proxy: CordaRPCOps
        private set

    @PostConstruct
    fun initialiseNodeRPCConnection() {
            val rpcAddress = NetworkHostAndPort(host, rpcPort)
            val rpcClient = CordaRPCClient(rpcAddress)
            val rpcConnection = rpcClient.start(username, password)
            proxy = rpcConnection.proxy
    }

    @PreDestroy
    override fun close() {
        rpcConnection.notifyServerAndClose()
    }
}
Controller.kt

ここでは各Httpリクエストに対してどの処理を行うかをコーデイングします。
このControllerのフィールドに先ほど定義したNodeRPCConnectionクラスを指定し、spring bootによりDI(Dependency Injection)されます。
このNodeRPCConnectionのプロパティのリモートインターフェースを使ってRPC操作を行います。

下記コードにおけるproxy.startTrackedFlowはFlowを実行するRPC操作です。

**
 * Define your API endpoints here.
 */
@RestController
@RequestMapping("/") // The paths for HTTP requests are relative to this base path.
class UserController(rpc: NodeRPCConnection) {

    companion object {
        private val logger = LoggerFactory.getLogger(RestController::class.java)
    }

    private val proxy = rpc.proxy
    
    @PutMapping(value = [ "issueTokens" ], produces = [ TEXT_PLAIN_VALUE ])
    fun issueToken(@RequestParam(value = "accountName") accountName: String,
                 @RequestParam(value = "currency") currency:  String,
                 @RequestParam(value = "amount") amount: Long): ResponseEntity<String> {
    
        try {
            val accountName = accountName
            val currency = currency
            val amount = amount
    
            val result = proxy.startTrackedFlow(::IssueTokensFlow, accountName, currency, amount).returnValue.get()
            // Return the response.
            return ResponseEntity
                    .status(HttpStatus.CREATED)
                    .body(result)
    
        } catch (e: Exception) {
            return ResponseEntity
                    .status(HttpStatus.BAD_REQUEST)
                    .body(e.message)
    
        }
    }
    
}

アプリの起動と実行手順

前述の独自で作成したCorDappとフロントアプリをローカル環境で実行します。 今回はトークンの発行を画面を使って紹介します。

起動手順

  1. nodeの起動
    Webサーバの起動には先にノードを起動しておく必要があります。
    sampleのプロジェクトと同様にrunnodes.batでノードを起動します。
  2. Webサーバの起動
    BankA用のWebサーバを起動します。
    build.gradleに記述したタスクを実行します。gradlew runTemplateServerA
    次のように表示されていれば正しく起動できています。今回はローカル環境ですのでlocalhostと赤枠で囲ったポート番号へアクセスします。image-20220303143639146
    図4:Webサーバ起動確認

実行手順

  1. Web画面表示
    ブラウザからアクセスすると画面が表示されます。
    画面が表示されない場合はノードのRPC設定に誤りがある可能性があるのでWebサーバの起動パラメータとnode.confのrpc設定が一致しているか確認してください。
    image-20220303145524101
    図5:初期画面
  2. トークンの発行
    発行ボタンを押下するとController.ktissueTokenが呼び出されます。
    image-20220303152441146
    図6:トークンの発行画面
    このissueTokenのレスポンスとして以下の画面が返されます。
    ここで発行した分のトークンが表示されます。
    image-20220303153257734
    図7:トークンの発行後の画面

おわりに

今回はspring bootを使ったCordaのWebアプリケーションの紹介とフロントとCorDappをRPCで接続する方法について紹介しました。
spring bootを使った構築はCordaのドキュメントにも構築方法が記載されているので、これらを使ってWebアプリケーションを作成することがおすすめです。
本記事がCordaのWebアプリケーション開発の参考になれば幸いです。