はじめに
どーも、のぶこふです。
前回、前々回に引き続き、Cordaのチュートリアルを進めていきます。
今回のチュートリアルでは、コントラクトに制約をかけていきます(制約に制約?)。
→IOUを発行する際、IOUをブロックチェーン上に作成する前に、貸し手と借り手の両方から合意をとるようにします。
前回の内容に追記していく形を取るので、事前準備はそちらを御覧ください。
今回も言語はKotlinを使用していきます。
契約書を書く
▼IOUContractを定義する
package com.template.contracts
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.transactions.LedgerTransaction
// 1:importを追加します:
import com.template.states.IOUState
import net.corda.core.contracts.*
// ************
// * Contract *
// ************
// 2:元の記述を削除(比較用に、ここではコメントアウト)します:
//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
// }
//}
// 3:下記内容を追加します:
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))
}
}
}
▼ファイル名を変更します
mv contracts/src/main/kotlin/com/template/contracts/TemplateContract.kt contracts/src/main/kotlin/com/template/contracts/IOUContract.kt
▼Contractの変更に合わせて、他ファイルも修正します
(前回での修正箇所に該当するコメント等は削除しています)
package com.template.states
// 1:インポートを「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 *
// *********
// 2:「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)
}
フローを更新する
トランザクションを確認する
▼importを追加します
下記を追加します
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.SignedTransaction
▼IOUFlowのcallとIOUFlowResponderのcallを変更する
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() {
// 1:元の記述を削除(比較用に、ここではコメントアウト)します:
// // 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))
// 2:下記内容を追加します:
// 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>() {
// 3:元の記述を削除(比較用に、ここではコメントアウト)します:
// @Suspendable
// override fun call() {
// subFlow(ReceiveFinalityFlow(otherPartySession))
// }
// 4:下記内容を追加します:
@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))
}
}
確認
▼デプロイ
$ ./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
▼ノードを実行する
$ 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のタブで実行
▼ノードの確認
>>> 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の確認
>>> 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送る)
>>> 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確認
>>> 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.
>>> 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.
>>> 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.
>>> 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}
おまけ2
間違えて、次の章「契約書を書く」にも手を出した時に躓いた事。
なお、次章は既にあるライブラリの中身を見てみよう的な無いようなので、自身でコードを書く必要はなかった。
- 「adminAddressが無い」的なエラーが出た
- build.gradleの各ノードにrpcSettingsを追加する
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に下記を追加する
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に下記を追加する
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"
おわりに
今回は、チュートリアルを元にコントラクトに簡単な修正を加えました。
簡単なコントラクトであれば、テンプレートを修正するだけでも十分使えそうですね。
チュートリアルを進めていくのは個人的にツラミなので、もうちょっと動くベースでゴニョゴニョしていきたい。
今回は以上です。
ありがとうございました。