3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CordaでRPCを利用してFlowを実行(IOUを発行)する。

Posted at

どーも、のぶこふです。

今回は、RPCを利用して、Flowの実行(IOUの発行)を行います。

手順としては、下記の通りです。

  1. 公式からテンプレをクローン
  2. ファイル修正
  3. ノード起動
  4. RPC実行

では、一つずつ実施していきます。

0.開発環境

私の環境は以下の通りです。

  • Mac High Sierra(10.13.6)
  • IntelliJ(2019.1.2)

なお、修正対象のファイルはGithubに配置しているので、問題がありましたら、そちらも御覧ください。

1.テンプレからクローン

ディレクトリを作成するよ
$ mkdir cordaRPCTest
$ cd cordaRPCTest/
公式Githubからクローンするよ
$ git clone https://github.com/corda/cordapp-template-kotlin.git
$ cd cordapp-template-kotlin/
確認するよ
$ ll
total 80
-rw-r--r--  1 nobkovskii  staff   579  6 24 15:01 LICENCE
-rw-r--r--  1 nobkovskii  staff  4798  6 24 15:01 README.md
-rw-r--r--  1 nobkovskii  staff   232  6 24 15:01 TRADEMARK
-rw-r--r--  1 nobkovskii  staff  4014  6 24 15:01 build.gradle
drwxr-xr-x  4 nobkovskii  staff   128  6 24 15:01 clients
drwxr-xr-x  4 nobkovskii  staff   128  6 24 15:01 config
drwxr-xr-x  4 nobkovskii  staff   128  6 24 15:01 contracts
drwxr-xr-x  3 nobkovskii  staff    96  6 24 15:01 gradle
-rw-r--r--  1 nobkovskii  staff    65  6 24 15:01 gradle.properties
-rwxr-xr-x  1 nobkovskii  staff  5296  6 24 15:01 gradlew
-rw-r--r--  1 nobkovskii  staff  2176  6 24 15:01 gradlew.bat
-rw-r--r--  1 nobkovskii  staff    57  6 24 15:01 settings.gradle
drwxr-xr-x  4 nobkovskii  staff   128  6 24 15:01 workflows

2.ファイル修正

修正するファイル一覧

  • ./workflows/src/main/kotlin/com/template/flows/Flows.kt
  • ./contracts/src/main/kotlin/com/template/states/TemplateState.kt
  • ./clients/src/main/kotlin/com/template/Client.kt

IntelliJでプロジェクトを開く

  1. IntelliJを開く
  2. Open > cordapp-template-kotlin/build.gradle を選択 > Open
  3. Open As Project > 「Use auto-import」にチェック > OK

Stateの修正

公式の手順通り修正していきます。
下記のコピペでOKです。
修正箇所は、コード内のコメントに記述してあります。

./contracts/src/main/kotlin/com/template/states/TemplateState.kt
package com.template.states

import com.template.contracts.TemplateContract
import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.ContractState
// import net.corda.core.identity.AbstractParty // <--- [ 削除 ]
import net.corda.core.identity.Party // <--- [ 追加 ]

// *********
// * State *
// *********
@BelongsToContract(TemplateContract::class)
data class TemplateState(val value: Int,
                         val lender: Party,
                         val borrower: Party) : ContractState {
    override val participants get() = listOf(lender, borrower)
} // <--- [ 引数と処理を修正 ]

Flowの修正

Stateと同様、手順通り修正します。
こちら側は、まるっと修正しています。

./workflows/src/main/kotlin/com/template/flows/Flows.kt
import co.paralleluniverse.fibers.Suspendable
import com.template.contracts.TemplateContract
import com.template.states.TemplateState
import net.corda.core.contracts.Command
import net.corda.core.contracts.requireThat
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker

@InitiatingFlow
@StartableByRPC
class TemplateFlow(val iouValue: Int,
              val otherParty: Party) : FlowLogic<Unit>() {

    override val progressTracker = ProgressTracker()

    @Suspendable
    override fun call() {
        val notary = serviceHub.networkMapCache.notaryIdentities[0]

        val outputState = TemplateState(iouValue, ourIdentity, otherParty)
        val command = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)

        val txBuilder = TransactionBuilder(notary = notary)
                .addOutputState(outputState, TemplateContract.ID)
                .addCommand(command)

        val signedTx = serviceHub.signInitialTransaction(txBuilder)

        val otherPartySession = initiateFlow(otherParty)

        subFlow(FinalityFlow(signedTx, otherPartySession))
    }
}

@InitiatedBy(TemplateFlow::class)
class TemplateFlowResponder(private val otherPartySession: FlowSession) : FlowLogic<Unit>() {
    @Suspendable
    override fun call() {
        val signTxFlow = object : SignTransactionFlow(otherPartySession){
            override fun checkTransaction(stx: SignedTransaction) = requireThat{
                val output = stx.tx.outputs.single().data
                val mys = output as TemplateState
            }
        }

        val expectedTxid = subFlow(signTxFlow).id
        subFlow(ReceiveFinalityFlow(otherPartySession,expectedTxid))
    }
}

Clientの修正

このファイルでRPCで呼び出すFlowを指定します。(今回は固定Flow)

./clients/src/main/kotlin/com/template/Client.kt
package com.template

import TemplateFlow  // <--- [ 追加 ]
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.startTrackedFlow  // <--- [ 追加 ]
import net.corda.core.utilities.NetworkHostAndPort.Companion.parse
import net.corda.core.utilities.loggerFor

/**
 * Connects to a Corda node via RPC and performs RPC operations on the node.
 *
 * The RPC connection is configured using command line arguments.
 */
fun main(args: Array<String>) = Client().main(args)

private class Client {
    companion object {
        val logger = loggerFor<Client>()
    }

    fun main(args: Array<String>) {
        // Create an RPC connection to the node.
        require(args.size == 3) { "Usage: Client <node address> <rpc username> <rpc password>" }
        val nodeAddress = parse(args[0])
        val rpcUsername = args[1]
        val rpcPassword = args[2]
        val client = CordaRPCClient(nodeAddress)
        val proxy = client.start(rpcUsername, rpcPassword).proxy

        // Interact with the node.
        // For example, here we print the nodes on the network.
        val nodes = proxy.networkMapSnapshot()
        logger.info("{}", nodes)

        // PartyA -> Value 1 -> PartyB
        proxy.startTrackedFlow(::TemplateFlow,1,nodes[2].legalIdentities[0]) // <--- [ 追加 ]
    }
}

3.ノード起動

デプロイするよ
$ pwd
/cordaRPCTest/cordapp-template-kotlin
$ ./gradlew clean deployNodes
〜略〜
BUILD SUCCESSFUL in 37s
18 actionable tasks: 14 executed, 4 up-to-date
ノード起動(全て起動するまで、操作禁止!!)
$ ./build/nodes/runnodes

ノード確認

開かれたタブの中で、PartyAのタブを開きます。
画面上部あたりのログか、Welcome to the Corda interactive shell.と書かれているすぐ上あたりを見ると、どのノードなのかがわかるかと思います。

PartyAで実行
bash -c 'cd "/cordaRPCTest/cordapp-template-kotlin/build/nodes/PartyA" ; 

〜略〜

   ______               __
  / ____/     _________/ /___ _
 / /     __  / ___/ __  / __ `/         People used to laugh at me when I said I wanted
/ /___  /_/ / /  / /_/ / /_/ /          to be a comedian. Well they're not laughing now!
\____/     /_/   \__,_/\__,_/

--- Corda Open Source 4.0 (503a2ff) -------------------------------------------------------------

〜略〜
Node for "PartyA" started up and registered in 26.03 sec


Welcome to the Corda interactive shell.
Useful commands include 'help' to see what is available, and 'bye' to shut down the node.
PartyAで実行
// Flow一覧を表示する
Mon Jun 24 16:02:46 JST 2019>>>  flow list
TemplateFlow
net.corda.core.flows.ContractUpgradeFlow$Authorise
net.corda.core.flows.ContractUpgradeFlow$Deauthorise
net.corda.core.flows.ContractUpgradeFlow$Initiate

// ネットワークに登録されているノード一覧を表示する
Mon Jun 24 16:03:16 JST 2019>>> run networkMapSnapshot
- addresses:
  - "localhost:10005"
  legalIdentitiesAndCerts:
  - "O=PartyA, L=London, C=GB"
  platformVersion: 4
  serial: 1561359724664
- addresses:
  - "localhost:10002"
  legalIdentitiesAndCerts:
  - "O=Notary, L=London, C=GB"
  platformVersion: 4
  serial: 1561359724945
- addresses:
  - "localhost:10008"
  legalIdentitiesAndCerts:
  - "O=PartyB, L=New York, C=US"
  platformVersion: 4
  serial: 1561359722697
PartyAで実行
// 保持しているStateを確認する
Mon Jun 24 16:03:23 JST 2019>>> run vaultQuery contractStateType: com.template.states.TemplateState
states: []
statesMetadata: []
totalStatesAvailable: -1
stateTypes: "UNCONSUMED"
otherResults: []

無事、ノードが起動されていることが確認できました。
うまくノードが起動できていなかった場合は、再度デプロイ→起動を試してみてください

4.RPC実行

RPCは、最初のターミナルから実行します。

ターミナルでRPCを実行するよ
$ ./gradlew runTemplateClient

> Task :clients:runTemplateClient
I 16:13:40 1 RPCClient.logElapsedTime - Startup took 1143 msec
   I 16:13:40 1 Client.main - [NodeInfo(addresses=[localhost:10005], legalIdentitiesAndCerts=[O=PartyA, L=London, C=GB], platformVersion=4, serial=1561359724664), NodeInfo(addresses=[localhost:10002], legalIdentitiesAndCerts=[O=Notary, L=London, C=GB], platformVersion=4, serial=1561359724945), NodeInfo(addresses=[localhost:10008], legalIdentitiesAndCerts=[O=PartyB, L=New York, C=US], platformVersion=4, serial=1561359722697)]

BUILD SUCCESSFUL in 7s
14 actionable tasks: 7 executed, 7 up-to-date

再びPartyAのタブに戻り、Stateを確認します。

PartyAで実行
Mon Jun 24 16:15:21 JST 2019>>> run vaultQuery contractStateType: com.template.states.TemplateState
states:
- state:
    data: !<com.template.states.TemplateState>
      value: 1
      lender: "O=PartyA, L=London, C=GB"
      borrower: "O=PartyB, L=New York, C=US"
    contract: "com.template.contracts.TemplateContract"
    notary: "O=Notary, L=London, C=GB"
    encumbrance: null
    constraint: !<net.corda.core.contracts.SignatureAttachmentConstraint>
      key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
  ref:
    txhash: "73E84ECA842336D73E9A648F6900049545A12033D92DDCB59511AAA9E3597E79"
    index: 0
statesMetadata:
- ref:
    txhash: "73E84ECA842336D73E9A648F6900049545A12033D92DDCB59511AAA9E3597E79"
    index: 0
  contractStateClassName: "com.template.states.TemplateState"
  recordedTime: "2019-06-24T07:13:43.489Z"
  consumedTime: null
  status: "UNCONSUMED"
  notary: "O=Notary, L=London, C=GB"
  lockId: null
  lockUpdateTime: null
  relevancyStatus: "RELEVANT"
  constraintInfo:
    constraint:
      key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
totalStatesAvailable: -1
stateTypes: "UNCONSUMED"
otherResults: []

PartyAからPartyBへNotaryを介してIOU(Value:1)が発行されているのがわかるかと思います。
↓の部分

states:
- state:
    data: !<com.template.states.TemplateState>
      value: 1
      lender: "O=PartyA, L=London, C=GB"
      borrower: "O=PartyB, L=New York, C=US"
    contract: "com.template.contracts.TemplateContract"
    notary: "O=Notary, L=London, C=GB"

RPC実行で使用したrunTemplateClientのtaskは、/clients/build.gradleにあります。

/clients/build.gradle
task runTemplateClient(type: JavaExec, dependsOn: assemble) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'com.template.ClientKt'
    args 'localhost:10006', 'user1', 'test'
}

5.おわりに

PartyAのタブからでもFlowは実行できますが、このようにRPCを使ってFlowを実行することができ、より実用的?な内容だったかと思います。

まだまだ知らないことが多いです。
先日までドキュメントを片っ端から読んでまとめていて、改めてサンプルを実行したら「こういう仕組みだったのか」と、自分の浅はかさを実感することが出来ました。
ドキュメント読むの大事。
とはいえ、今回修正したファイル以外だと、いじるのはコントラクトのファイルくらいだと思うので、これらをどう組み合わせていくかー?に頭を悩ませていきそうです。

今回は以上です、ありがとうございました。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?