Corda-Tutorial_HelloWorld pt2

## はじめに
どーも、のぶこふです。
[前回](https://qiita.com/nobkovskii/items/28da189a0d71a1b7d6e7)、[前々回](https://qiita.com/nobkovskii/items/7c754878841fcf30a559)に引き続き、Cordaのチュートリアルを進めていきます。

[今回のチュートリアル](https://docs.corda.net/releases/release-V4.0/tut-two-party-introduction.html)では、コントラクトに制約をかけていきます（制約に制約？）。
→IOUを発行する際、IOUをブロックチェーン上に作成する前に、貸し手と借り手の両方から合意をとるようにします。

前回の内容に追記していく形を取るので、事前準備はそちらを御覧ください。
今回も言語はKotlinを使用していきます。

## 契約書を書く
https://docs.corda.net/releases/release-V4.0/tut-two-party-contract.html

▼IOUContractを定義する

```kotlin:com.template.contracts.TemplateContract.kt
package com.template.contracts

import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.transactions.LedgerTransaction

// １：importを追加します:
import com.template.states.IOUState
import net.corda.core.contracts.*

// ************
// * Contract *
// ************
// ２：元の記述を削除（比較用に、ここではコメントアウト）します：
//class TemplateContract : Contract {
//    companion object {
//        // Used to identify our contract when building a transaction.
//        const val ID = "com.template.contracts.TemplateContract"
//    }
//
//    // A transaction is valid if the verify() function of the contract of all the transaction's input and output states
//    // does not throw an exception.
//    override fun verify(tx: LedgerTransaction) {
//        // Verification logic goes here.
//    }
//
//    // Used to indicate the transaction's intent.
//    interface Commands : CommandData {
//        class Action : Commands
//    }
//}

// ３：下記内容を追加します:
class IOUContract : Contract {
    companion object {
        // ★チュートリアル通りだと、パスが微妙に足りない
        // const val ID = "com.template.IOUContract"
        const val ID = "com.template.contracts.IOUContract"
    }

    // Our Create command.
    class Create : CommandData

    override fun verify(tx: LedgerTransaction) {
        val command = tx.commands.requireSingleCommand<Create>()

        requireThat {
            // Constraints on the shape of the transaction.
            "No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
            "There should be one output state of type IOUState." using (tx.outputs.size == 1)

            // IOU-specific constraints.
            val output = tx.outputsOfType<IOUState>().single()
            "The IOU's value must be non-negative." using (output.value > 0)
            "The lender and the borrower cannot be the same entity." using (output.lender != output.borrower)

            // Constraints on the signers.
            val expectedSigners = listOf(output.borrower.owningKey, output.lender.owningKey)
            "There must be two signers." using (command.signers.toSet().size == 2)
            "The borrower and lender must be signers." using (command.signers.containsAll(expectedSigners))
        }
    }
}
```

▼ファイル名を変更します

```console:Rename_File-name
mv contracts/src/main/kotlin/com/template/contracts/TemplateContract.kt contracts/src/main/kotlin/com/template/contracts/IOUContract.kt
```

▼Contractの変更に合わせて、他ファイルも修正します
（前回での修正箇所に該当するコメント等は削除しています）

```kotlin:com.template.states.IOUState.kt
package com.template.states

// １：インポートを「TemplateContract」から「IOUContract」に変更します
import com.template.contracts.IOUContract
import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party

// *********
// * State *
// *********
// ２：「TemplateContract」から「IOUContract」に変更します
@BelongsToContract(IOUContract::class)
data class IOUState(val value: Int,
               val lender: Party,
               val borrower: Party) : ContractState {
    override val participants get() = listOf(lender, borrower)
}

```


## フローを更新する
https://docs.corda.net/releases/release-V4.0/tut-two-party-flow.html

### トランザクションを確認する
▼importを追加します
下記を追加します

```kotlin:workflows/src/main/kotlin/com/template/flows/Flows.kt 
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.SignedTransaction
```

▼IOUFlowのcallとIOUFlowResponderのcallを変更する

```kotlin:workflows/src/main/kotlin/com/template/flows/Flows.kt 
package com.template.flows

import co.paralleluniverse.fibers.Suspendable
import com.template.contracts.IOUContract
import com.template.states.IOUState
import net.corda.core.flows.*
import net.corda.core.utilities.ProgressTracker
import net.corda.core.contracts.Command
import net.corda.core.contracts.requireThat
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder

// *********
// * Flows *
// *********
@InitiatingFlow
@StartableByRPC
class IOUFlow(val iouValue: Int,
              val otherParty: Party) : FlowLogic<Unit>() {

    /** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
    override val progressTracker = ProgressTracker()

    /** The flow logic is encapsulated within the call() method. */
    @Suspendable
    override fun call() {
        // １：元の記述を削除（比較用に、ここではコメントアウト）します：
//        // We retrieve the notary identity from the network map.
//        val notary = serviceHub.networkMapCache.notaryIdentities[0]
//
//        // We create the transaction components.
//        val outputState = IOUState(iouValue, ourIdentity, otherParty)
//        val command = Command(IOUContract.Commands.Action(), ourIdentity.owningKey)
//
//        // We create a transaction builder and add the components.
//        val txBuilder = TransactionBuilder(notary = notary)
//                .addOutputState(outputState, IOUContract.ID)
//                .addCommand(command)
//
//        // We sign the transaction.
//        val signedTx = serviceHub.signInitialTransaction(txBuilder)
//
//        // Creating a session with the other party.
//        val otherPartySession = initiateFlow(otherParty)
//
//        // We finalise the transaction and then send it to the counterparty.
//        subFlow(FinalityFlow(signedTx, otherPartySession))

        // ２：下記内容を追加します:
        // We retrieve the notary identity from the network map.
        val notary = serviceHub.networkMapCache.notaryIdentities[0]

        // We create the transaction components.
        val outputState = IOUState(iouValue, ourIdentity, otherParty)
        val command = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))

        // We create a transaction builder and add the components.
        val txBuilder = TransactionBuilder(notary = notary)
                .addOutputState(outputState, IOUContract.ID)
                .addCommand(command)

        // Verifying the transaction.
        txBuilder.verify(serviceHub)

        // Signing the transaction.
        val signedTx = serviceHub.signInitialTransaction(txBuilder)

        // Creating a session with the other party.
        val otherPartySession = initiateFlow(otherParty)

        // Obtaining the counterparty's signature.
        val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherPartySession), CollectSignaturesFlow.tracker()))

        // Finalising the transaction.
        subFlow(FinalityFlow(fullySignedTx, otherPartySession))
    }
}

@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(private val otherPartySession: FlowSession) : FlowLogic<Unit>() {
    // ３：元の記述を削除（比較用に、ここではコメントアウト）します：
//    @Suspendable
//    override fun call() {
//        subFlow(ReceiveFinalityFlow(otherPartySession))
//    }

    // ４：下記内容を追加します:
    @Suspendable
    override fun call() {
        val signTransactionFlow = object : SignTransactionFlow(otherPartySession) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                val output = stx.tx.outputs.single().data
                "This must be an IOU transaction." using (output is IOUState)
                val iou = output as IOUState
                "The IOU's value can't be too high." using (iou.value < 100)
            }
        }

        val expectedTxId = subFlow(signTransactionFlow).id
        subFlow(ReceiveFinalityFlow(otherPartySession, expectedTxId))
    }
}
```

## 確認
▼デプロイ

```console:Terminal=deploy-suruyo
$  ./gradlew clean deployNodes

> Task :deployNodes
Running Cordform task
Deleting /corda-sample-kotlin/cordapp-template-kotlin/build/nodes
Bootstrapping local test network in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes
Generating node directory for PartyA
Generating node directory for Notary
Generating node directory for PartyB
Waiting for all nodes to generate their node-info files...
Distributing all node-info files to all nodes
Loading existing network parameters... none found
Gathering notary identities
Generating contract implementations whitelist
New NetworkParameters {
      minimumPlatformVersion=4
      notaries=[NotaryInfo(identity=O=Notary, L=London, C=GB, validating=false)]
      maxMessageSize=10485760
      maxTransactionSize=524288000
      whitelistedContractImplementations {
        
      }
      eventHorizon=PT720H
      packageOwnership {
        
      }
      modifiedTime=2019-05-16T03:08:35.226Z
      epoch=1
  }
Bootstrapping complete!

BUILD SUCCESSFUL in 32s
18 actionable tasks: 17 executed, 1 up-to-date
```

▼ノードを実行する

```console:Terminal=Terminal=Node-wo-kidou-suruyo
$ build/nodes/runnodes

Starting nodes in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes
Starting corda.jar in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes/Notary on debug port 5005
Node will expose jolokia monitoring port on 7005
Running command: osascript -e tell app "Terminal"
    activate
    delay 0.5
    tell app "System Events" to tell process "Terminal" to keystroke "t" using command down
    delay 0.5
    do script "bash -c 'cd \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/Notary\" ; \"/Library/Java/JavaVirtualMachines/jdk1.8.0_212.jdk/Contents/Home/jre/bin/java\" \"-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -javaagent:drivers/jolokia-jvm-1.6.0-agent.jar=port=7005,logHandlerClass=net.corda.node.JolokiaSlf4jAdapter\" \"-Dname=Notary\" \"-jar\" \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/Notary/corda.jar\" && exit'" in selected tab of the front window
end tell
Starting corda.jar in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyA on debug port 5006
Node will expose jolokia monitoring port on 7006
Running command: osascript -e tell app "Terminal"
    activate
    delay 0.5
    tell app "System Events" to tell process "Terminal" to keystroke "t" using command down
    delay 0.5
    do script "bash -c 'cd \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyA\" ; \"/Library/Java/JavaVirtualMachines/jdk1.8.0_212.jdk/Contents/Home/jre/bin/java\" \"-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006 -javaagent:drivers/jolokia-jvm-1.6.0-agent.jar=port=7006,logHandlerClass=net.corda.node.JolokiaSlf4jAdapter\" \"-Dname=PartyA\" \"-jar\" \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyA/corda.jar\" && exit'" in selected tab of the front window
end tell
No file corda.jar found in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes/.cache
Starting corda.jar in /corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyB on debug port 5007
Node will expose jolokia monitoring port on 7007
Running command: osascript -e tell app "Terminal"
    activate
    delay 0.5
    tell app "System Events" to tell process "Terminal" to keystroke "t" using command down
    delay 0.5
    do script "bash -c 'cd \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyB\" ; \"/Library/Java/JavaVirtualMachines/jdk1.8.0_212.jdk/Contents/Home/jre/bin/java\" \"-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5007 -javaagent:drivers/jolokia-jvm-1.6.0-agent.jar=port=7007,logHandlerClass=net.corda.node.JolokiaSlf4jAdapter\" \"-Dname=PartyB\" \"-jar\" \"/corda-sample-kotlin/cordapp-template-kotlin/build/nodes/PartyB/corda.jar\" && exit'" in selected tab of the front window
end tell
Started 3 processes
Finished starting nodes
```

○イメージは省略

▼ノードと対話する
今回もPartyAのタブで実行

▼ノードの確認

```console:Terminal(PartyA)
>>> run networkMapSnapshot 
- addresses:
  - "localhost:10005"
  legalIdentitiesAndCerts:
  - "O=PartyA, L=London, C=GB"
  platformVersion: 4
  serial: 1557976113185
- addresses:
  - "localhost:10002"
  legalIdentitiesAndCerts:
  - "O=Notary, L=London, C=GB"
  platformVersion: 4
  serial: 1557976111004
- addresses:
  - "localhost:10008"
  legalIdentitiesAndCerts:
  - "O=PartyB, L=New York, C=US"
  platformVersion: 4
  serial: 1557976113112
```

▼Flowの確認

```console:Terminal(PartyA)
>>> flow list
com.template.flows.IOUFlow
net.corda.core.flows.ContractUpgradeFlow$Authorise
net.corda.core.flows.ContractUpgradeFlow$Deauthorise
net.corda.core.flows.ContractUpgradeFlow$Initiate
```

▼IOU作成（PartyBへ、IOUを99送る）

```console:Terminal(PartyA)
>>> start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"

 ✅   Starting
          Requesting signature by notary service
              Requesting signature by Notary service
              Validating response from Notary service
     ✅   Broadcasting transaction to participants
➡️   Done
Flow completed with result: kotlin.Unit
```

▼IOU確認

```console:Terminal(PartyA)
>>> run vaultQuery contractStateType: com.template.states.IOUState
states:
- state:
    data: !<com.template.states.IOUState>
      value: 99
      lender: "O=PartyA, L=London, C=GB"
      borrower: "O=PartyB, L=New York, C=US"
    contract: "com.template.contracts.IOUContract"
    notary: "O=Notary, L=London, C=GB"
    encumbrance: null
    constraint: !<net.corda.core.contracts.SignatureAttachmentConstraint>
      key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
  ref:
    txhash: "90B255F02E35E290EC9F5F18441C846C234236854C41D6385B68BDB5A6FBCA76"
    index: 0
- state:
    data: !<com.template.states.IOUState>
      value: 99
      lender: "O=PartyA, L=London, C=GB"
      borrower: "O=PartyB, L=New York, C=US"
    contract: "com.template.contracts.IOUContract"
    notary: "O=Notary, L=London, C=GB"
    encumbrance: null
    constraint: !<net.corda.core.contracts.SignatureAttachmentConstraint>
      key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
  ref:
    txhash: "18377678E6C71331FF754A48DF25B88BB87905ABB6110A13D86D487235CDC160"
    index: 0
statesMetadata:
- ref:
    txhash: "90B255F02E35E290EC9F5F18441C846C234236854C41D6385B68BDB5A6FBCA76"
    index: 0
  contractStateClassName: "com.template.states.IOUState"
  recordedTime: "2019-05-16T03:33:32.485Z"
  consumedTime: null
  status: "UNCONSUMED"
  notary: "O=Notary, L=London, C=GB"
  lockId: null
  lockUpdateTime: null
  relevancyStatus: "RELEVANT"
  constraintInfo:
    constraint:
      key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
- ref:
    txhash: "18377678E6C71331FF754A48DF25B88BB87905ABB6110A13D86D487235CDC160"
    index: 0
  contractStateClassName: "com.template.states.IOUState"
  recordedTime: "2019-05-16T04:42:13.524Z"
  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: []
```

無事、実行することができました。

### おまけ
IOU作成の箇所で、「iouValue」を100以上や0以下にしたり、「otherParty」を存在しなかったりPartyAを指定するなどすると、エラーが発生します。
エラー時のメッセージはIOUContractのverifyで指定したモノです。

* 100以上を指定
    * The IOU's value can't be too high.

```console:Terminal(PartyA)
>>> start IOUFlow iouValue: 100, otherParty: "O=PartyB,L=New York,C=US"    

 ✅   Starting
    ➡️   Collecting signatures from counterparties.
        Verifying collected signatures.

 ✅   Starting
 ✅   Starting
    ➡️   Collecting signatures from counterparties.
    🚫   Verifying collected signatures.
🚫   Done
 ☠   java.lang.IllegalArgumentException: Failed requirement: The IOU's value can't be too high.
[ERROR] 13:51:20+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1lf0ke5, moreInformationAt=https://errors.corda.net/OS/4.0/1lf0ke5] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000003, flow-id=09e47ca9-efb9-48e6-80ee-b84e8144107d, invocation_id=692be789-43c0-45a1-9cd0-961e1083e06a, invocation_timestamp=2019-05-16T04:51:19.934Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 13:51:20+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1lf0ke5, moreInformationAt=https://errors.corda.net/OS/4.0/1lf0ke5] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000003, flow-id=09e47ca9-efb9-48e6-80ee-b84e8144107d, invocation_id=692be789-43c0-45a1-9cd0-961e1083e06a, invocation_timestamp=2019-05-16T04:51:19.934Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 13:51:20+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1lf0ke5, moreInformationAt=https://errors.corda.net/OS/4.0/1lf0ke5] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000003, flow-id=09e47ca9-efb9-48e6-80ee-b84e8144107d, invocation_id=692be789-43c0-45a1-9cd0-961e1083e06a, invocation_timestamp=2019-05-16T04:51:19.934Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
```

* 0以下を指定
    * The IOU's value must be non-negative.

```console:Terminal(PartyA)
>>> start IOUFlow iouValue: 0, otherParty: "O=PartyB,L=New York,C=US"  

➡️   Starting
    Done
[ERROR] 14:07:19+0900 [Node thread-1] internal.Verifier.verifyContracts - Error validating transaction 4B39B4C8773878381E4BAC6DFEA37B92236BFE458F5940B523574882B13E9A52. [errorCode=uojrqm, moreInformationAt=https://errors.corda.net/OS/4.0/uojrqm] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000008, flow-id=5121bf3a-6624-46f0-b33c-3e67e9a19cb3, invocation_id=c37fd9cf-9e83-4f70-86c7-14186976908e, invocation_timestamp=2019-05-16T05:07:19.927Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:07:19+0900 [Node thread-1] internal.Verifier.verifyContracts - Error validating transaction 4B39B4C8773878381E4BAC6DFEA37B92236BFE458F5940B523574882B13E9A52. [errorCode=uojrqm
➡️   Starting
🚫   Done
➡️   Starting
🚫   Done
 ☠   net.corda.core.contracts.TransactionVerificationException$ContractRejection: Contract verification failed: Failed requirement: The IOU's value must be non-negative., contract: com.template.contracts.IOUContract, transaction: 4B39B4C8773878381E4BAC6DFEA37B92236BFE458F5940B523574882B13E9A52
[ERROR] 14:07:19+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000008, flow-id=5121bf3a-6624-46f0-b33c-3e67e9a19cb3, invocation_id=c37fd9cf-9e83-4f70-86c7-14186976908e, invocation_timestamp=2019-05-16T05:07:19.927Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:07:19+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000008, flow-id=5121bf3a-6624-46f0-b33c-3e67e9a19cb3, invocation_id=c37fd9cf-9e83-4f70-86c7-14186976908e, invocation_timestamp=2019-05-16T05:07:19.927Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:07:19+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000008, flow-id=5121bf3a-6624-46f0-b33c-3e67e9a19cb3, invocation_id=c37fd9cf-9e83-4f70-86c7-14186976908e, invocation_timestamp=2019-05-16T05:07:19.927Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:07:19+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000008, flow-id=5121bf3a-6624-46f0-b33c-3e67e9a19cb3, invocation_id=c37fd9cf-9e83-4f70-86c7-14186976908e, invocation_timestamp=2019-05-16T05:07:19.927Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
```

* 同じParty(PartyA)を指定
    * The lender and the borrower cannot be the same entity.

```console:Terminal(PartyA)
>>> start IOUFlow iouValue: 10, otherParty: "O=PartyA,L=London,C=GB"  

➡️   Starting
    Done
[ERROR] 14:12:08+0900 [Node thread-1] internal.Verifier.verifyContracts - Error validating transaction 8EF8934ED1849E6AC7E331C8FF80CB9121B93F2D363369E69904C13D6AE24B9F. [errorCode=uojrqm, moreInformationAt=https://errors.corda.net/OS/4.0/uojrqm] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000010, flow-id=3070e97b-5ed9-4991-b213-323372e35a47, invocation_id=531fcc67-33e3-48b7-9697-7deb1ee24ff7, invocation_timestamp=2019-05-16T05:12:08.349Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:12:08+0900 [Node thread-1] internal.Verifier.verifyContracts - Error validating transaction 8EF8934ED1849E6AC7E331C8FF80CB9121B93F2D363369E69904C13D6AE24B9F. [errorCode=uojrqm
➡️   Starting
🚫   Done
➡️   Starting
🚫   Done
 ☠   net.corda.core.contracts.TransactionVerificationException$ContractRejection: Contract verification failed: Failed requirement: The lender and the borrower cannot be the same entity., contract: com.template.contracts.IOUContract, transaction: 8EF8934ED1849E6AC7E331C8FF80CB9121B93F2D363369E69904C13D6AE24B9F
[ERROR] 14:12:08+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000010, flow-id=3070e97b-5ed9-4991-b213-323372e35a47, invocation_id=531fcc67-33e3-48b7-9697-7deb1ee24ff7, invocation_timestamp=2019-05-16T05:12:08.349Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:12:08+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000010, flow-id=3070e97b-5ed9-4991-b213-323372e35a47, invocation_id=531fcc67-33e3-48b7-9697-7deb1ee24ff7, invocation_timestamp=2019-05-16T05:12:08.349Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:12:08+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000010, flow-id=3070e97b-5ed9-4991-b213-323372e35a47, invocation_id=531fcc67-33e3-48b7-9697-7deb1ee24ff7, invocation_timestamp=2019-05-16T05:12:08.349Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}
[ERROR] 14:12:08+0900 [Node thread-1] proxies.ExceptionSerialisingRpcOpsProxy.log - Error during RPC invocation [errorCode=1hw1opl, moreInformationAt=https://errors.corda.net/OS/4.0/1hw1opl] {actor_id=internalShell, actor_owning_identity=O=PartyA, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000010, flow-id=3070e97b-5ed9-4991-b213-323372e35a47, invocation_id=531fcc67-33e3-48b7-9697-7deb1ee24ff7, invocation_timestamp=2019-05-16T05:12:08.349Z, origin=internalShell, session_id=013680ef-8eea-4eb5-a9d9-91240f743679, session_timestamp=2019-05-16T04:41:47.010Z, thread-id=150}

```

## おまけ２
間違えて、次の章「[契約書を書く](https://docs.corda.net/releases/release-V4.0/tutorial-contract.html)」にも手を出した時に躓いた事。
なお、次章は既にあるライブラリの中身を見てみよう的な無いようなので、自身でコードを書く必要はなかった。

* 「adminAddressが無い」的なエラーが出た
    * build.gradleの各ノードにrpcSettingsを追加する

```gradle:build.gradle
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
〜略〜
    node {
        name "O=Notary,L=London,C=GB"
        notary = [validating : false]
        p2pPort 10002
        rpcSettings {
            address("localhost:10003")
            adminAddress("localhost:10043")
        }
    }
```

* 「corda-webserver JAR が無い」的なエラーが出た
    * build.gradleのdependenciesに下記を追加する

```gradle:build.gradle
dependencies {
    // Corda dependencies.
    cordaCompile "$corda_release_group:corda-core:$corda_release_version"
    cordaCompile "$corda_release_group:corda-node-api:$corda_release_version"
    cordaRuntime "$corda_release_group:corda:$corda_release_version"

    // 下記を追加する
    cordaRuntime "$corda_release_group:corda-webserver:$corda_release_version"
    cordaCompile "$corda_release_group:corda-webserver-impl:$corda_release_version"
```

* IntelliJでexternal librariesから外部Jarを使う
    * 対象のbuild.gradleのdependenciesに下記を追加する

```gradle:contracts/build.gradle
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

    // Corda dependencies.
    cordaCompile "$corda_release_group:corda-core:$corda_release_version"

    // 下記を追加する
    cordaCompile "$corda_release_group:corda-finance-contracts:$corda_release_version"
    cordaCompile "$corda_release_group:corda-finance-workflows:$corda_release_version"
```

## おわりに

今回は、チュートリアルを元にコントラクトに簡単な修正を加えました。
簡単なコントラクトであれば、テンプレートを修正するだけでも十分使えそうですね。

チュートリアルを進めていくのは個人的にツラミなので、もうちょっと動くベースでゴニョゴニョしていきたい。

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