どーも、のぶこふです。
最近、Cordaのノード起動する度に、PCがウィンウィンいうので「PC頑張れっ!みんながんばれ!むしろ、ガンガンいこうぜ!!」と心の中で叫んでいます。
今回は、引き続きVaultQueryの使用方法と、JDBCを利用したSQLの発行方法について書きます。
VaultQueryの使用方法については、前々回の記事([Corda]VaultQueryを使って、Vaultを検索したり、H2DBを使ったり)と内容が重複しますので、その時のソースが手元にある場合は、そちらをお使いください。
(SchemaとStateは流用しています)
今回の記事 is 何?
- VaultQueryの使用方法がわかる
- JDBCを利用したSQLの発行方法がわかる
- するとどうなる???
- →TransactionのInputとして利用できる!!
はじめに
お約束のCordaテンプレートを使用します。
なお、今回使用PCはMacです。
環境準備
- 適当にディレクトリを作成します。(省略可)
- テンプレートをgit clone します。
- IDEを起動します。
$ mkdir vaultTest
$ cd vaultTest/
$ git clone https://github.com/corda/cordapp-template-kotlin.git
Cloning into 'cordapp-template-kotlin'...
remote: Enumerating objects: 39, done.
remote: Counting objects: 100% (39/39), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 3480 (delta 7), reused 25 (delta 0), pack-reused 3441
Receiving objects: 100% (3480/3480), 2.21 MiB | 2.37 MiB/s, done.
Resolving deltas: 100% (1259/1259), done.
Schemaの作成
- DB(インメモリ=H2DB)に独自Stateの情報を保存出来るように、Schemaを作成します。
- contracts/src/main/kotlin/com/templateにschemasディレクトリを作成し、その下にTemplateSchema.ktを作成します。
package com.template.schemas
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Table
object TemplateSchema
object TemplateSchemaV1 : MappedSchema(
schemaFamily = TemplateSchema.javaClass,
version = 1,
mappedTypes = listOf(TemplateSchemaV1.PersistentTemplate::class.java)
) {
@Entity
@Table(name = "template_state")
class PersistentTemplate(
@Column(name = "id")
var id: Int,
@Column(name = "data")
var data: String,
@Column(name = "counterParty")
var counterParty: String,
@Column(name = "issuer")
var issuer: String
) : PersistentState() {
constructor() : this(0,"","","")
}
}
Stateの修正
- 検索が出来るようにQueryableStateの実装と、Stateを一意に識別出来るようにLinearStateを実装します。
package com.template.states
import com.template.contracts.TemplateContract
import com.template.schemas.TemplateSchemaV1
import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
// *********
// * State *
// *********
@BelongsToContract(TemplateContract::class)
data class TemplateState(
val issuer :Party,
val counterParty: Party,
val id : Int,
val data: String,
override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val participants: List<AbstractParty> = listOf(issuer,counterParty)
) : LinearState, QueryableState {
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when(schema){
is TemplateSchemaV1 -> TemplateSchemaV1.PersistentTemplate(this.id,this.data,counterParty.name.toString(),issuer.name.toString())
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(TemplateSchemaV1)
}
Flowの修正
▼VaultQueryの実装
- Initiatorの引数を変更します。
- この引数を使って、Outputを作成します。
-
: FlowLogic<Unit>()
は、callの戻り値を指定しています。- callの戻り値がUnitなら上記のように、Stringなら
: FlowLogic<String>()
のように記述します。ここに関しては制限は無いので、自由に記述することが可能です。 - callの結果を他のシステムに連携する時などは、戻り値は必須ですね。
- callの戻り値がUnitなら上記のように、Stringなら
class Initiator(private val counterParty: Party, private val id: Int, private val data: String) : FlowLogic<Unit>() {
- Notaryの取得と、VaultQueryの設定を行います。
- Notaryは
serviceHub
のnetworkMapCache
から取得します。 -
VaultQueryCriteria
にVault.StateStatus.UNCONSUMED
と指定することで、ステータスが未消費のStateに絞り込みます。-
Vault.StateStatus.CONSUMED
なら消費済み -
Vault.StateStatus.ALL
なら全て -
Vault.StateStatus.UNCONSUMED
か、未指定の場合は、未消費のStateが対象になります - ただし、以下のbuilderで検索条件の絞り込みを行うと、デフォルトの
Vault.StateStatus.UNCONSUMED
に上書きされてしまうようです。
-
-
val condition1 = TemplateSchemaV1.PersistentTemplate::id.equal(id)
で使用しているのはnet.corda.core.node.services.vault.builder
のequal()
です。kotlin.any
で提供しているequals()
では無いので注意が必要です。- たまにサジェストをミスってエラーが発生して「???」となる
- Notaryは
@Suspendable
override fun call() {
// デフォルトのNotaryを指定
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// 未使用のState、かつIDが等しいStateを検索
val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)
val states = builder {
val condition1 = TemplateSchemaV1.PersistentTemplate::id.equal(id)
val q1 = QueryCriteria.VaultCustomQueryCriteria(condition1)
val criteria = generalCriteria.and(q1)
serviceHub.vaultService.queryBy<TemplateState>(criteria).states
}
- 続いてcallにOutput、Command、Transactionを作成します。
- 引数を使用しOutput用のStateを作成します。
- CommandはデフォルトのActionを使用します。
- 何もしていない空のコントラクトです
- Transactionには、作成したOutputとCommandを指定します。
- inputを指定する場合は
.addInputState(StateAndRef<*>)
を指定します。 -
StateAndRef<*>
は、ざっくりとqueryBy()
の戻り値と認識しておけばOKです。 -
TransactionBuilder
のadd~
は順序に指定はないので、先にInputをAddするなどもOKです。
- inputを指定する場合は
- 最後の
txBuilder.verify(serviceHub)
で、Commandが正しく設定されているか?などTransactionのチェックを行っています。
// Create Output
val output = TemplateState(issuer = ourIdentity,counterParty = counterParty,id= this.id,data = this.data)
// Create command
val cmd =
Command(TemplateContract.Commands.Action(), listOf(ourIdentity.owningKey, counterParty.owningKey))
// Create Transaction
val txBuilder = TransactionBuilder(notary)
.addOutputState(output)
.addCommand(cmd)
// Add Input
if (states.isNotEmpty()){
txBuilder.addInputState(states.first())
}
// Verify Transaction
txBuilder.verify(serviceHub)
- 最後にTransactionに自身の署名、他者の署名、Notaryの署名&二重支払いチェックを行い、Transactionを確定させます。
-
signInitialTransaction(txBuilder)
で自身の署名を取得します。 -
initiateFlow(counterParty)
で他者のセッションを起動し、subFlow
としてCollectSignaturesFlow(signedTx, setOf(counterPartySession))
で他者の署名を取得します。 -
FinalityFlow(fullySignedTx, setOf(counterPartySession))
でNotaryの署名&二重支払いチェックを行い、Transactionを確定させています。
-
// Signing Transaction
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Gathering Signs
val counterPartySession = initiateFlow(counterParty)
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, setOf(counterPartySession)))
// Finalize Transaction
subFlow(FinalityFlow(fullySignedTx, setOf(counterPartySession)))
}
- おっと、Responderの設定も必要でした。
-
Responder
はInitiator
のCollectSignaturesFlow
を受けて実行されます。 - 未実装の場合は
net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=XXX) with empty buffer
のようなエラーが発生します。
-
@InitiatedBy(Initiator::class)
class Responder(val counterPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(counterPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {}
}
val txId = subFlow(signTransactionFlow).id
subFlow(ReceiveFinalityFlow(counterPartySession, expectedTxId = txId))
}
}
- Flow.ktの全量は以下になります
Flow.ktの全量(長いので折りたたみ)
package com.template.flows
import co.paralleluniverse.fibers.Suspendable
import com.template.contracts.TemplateContract
import com.template.schemas.TemplateSchemaV1
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.node.services.Vault
import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.builder
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
// *********
// * Flows *
// *********
@InitiatingFlow
@StartableByRPC
class Initiator(private val counterParty: Party, private val id: Int, private val data: String) : FlowLogic<Unit>() {
override val progressTracker = ProgressTracker()
@Suspendable
override fun call() {
// デフォルトのNotaryを指定
val notary = serviceHub.networkMapCache.notaryIdentities[0]
id.equals(id)
val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)
val states = builder {
val condition1 = TemplateSchemaV1.PersistentTemplate::id.equal(id)
val q1 = QueryCriteria.VaultCustomQueryCriteria(condition1)
val criteria = generalCriteria.and(q1)
serviceHub.vaultService.queryBy<TemplateState>(criteria).states
}
// Create Output
val output = TemplateState(issuer = ourIdentity,counterParty = counterParty,id= this.id,data = this.data)
// Create command
val cmd =
Command(TemplateContract.Commands.Action(), listOf(ourIdentity.owningKey, counterParty.owningKey))
// Create Transaction
val txBuilder = TransactionBuilder(notary)
.addOutputState(output)
.addCommand(cmd)
// Add Input
if (states.isNotEmpty()){
txBuilder.addInputState(states.first())
}
txBuilder.verify(serviceHub)
// Signing Transaction
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Gathering Signs
val counterPartySession = initiateFlow(counterParty)
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, setOf(counterPartySession)))
// Finalize Transaction
subFlow(FinalityFlow(fullySignedTx, setOf(counterPartySession)))
}
}
@InitiatedBy(Initiator::class)
class Responder(val counterPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(counterPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {}
}
val txId = subFlow(signTransactionFlow).id
subFlow(ReceiveFinalityFlow(counterPartySession, expectedTxId = txId))
}
}
▼試しに動かす
- これで、VaultQueryが使える準備が出来ました。実際に動かして試してみましょう。
$ cd cordapp-template-kotlin/
$ ./gradlew deployNodes
$ ./build/nodes/runnodes
- ノードの起動まで終わったら、PartyAのノードで実行してみます。
- Stateに登録されていることがわかります。
>>> start Initiator counterParty: "PartyB" , id: 1 , data: "hoge"
✅ 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
>>> run vaultQuery contractStateType: com.template.states.TemplateState
states:
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 1
data: "hoge"
linearId:
externalId: null
id: "62bec7c3-7b64-4fbf-8d9e-3845a0142a43"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "C6D1F7540749A88E69F7A2D058C2E15B3D9BBE71E624A58F325FE073E64042B9"
index: 0
statesMetadata:
- ref:
txhash: "C6D1F7540749A88E69F7A2D058C2E15B3D9BBE71E624A58F325FE073E64042B9"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T01:49:26.643Z"
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: []
- 少し内容を変えて、実行してみます。
- 引数のdataをhogeからFugaに変更しています。
- こちらも無事実行されていますが、実行結果を見てお気づきの方もいらっしゃるかと思います。
>>> start Initiator counterParty: "PartyB" , id: 1 , data: "Fuga"
✅ 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
>>> run vaultQuery contractStateType: com.template.states.TemplateState
states:
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 1
data: "Fuga"
linearId:
externalId: null
id: "0332a33a-0c0d-4708-80e4-307b4322c68c"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "758D3F22DE3E48409E9E29113AEB49EB5B5037BDDEE842AF1C24416317F426DC"
index: 0
statesMetadata:
- ref:
txhash: "758D3F22DE3E48409E9E29113AEB49EB5B5037BDDEE842AF1C24416317F426DC"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T01:52:09.295Z"
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: []
- もう一度、内容を変更して実行してみます。
- idを1から2に、dataをFugaからBarに変更しています
-
run vaultQuery
の結果から見てわかる通り、件数が増えましたね。-
run vaultQuery
で表示しているのは、未消費のStateだけです。 - 2回目に実行した時は、1回目と同じIDを指定していたのでStateをInputとして消費しています。そのため、2回目に確認したときに、1回目の結果が表示されなかったのです。
- しかし、3回目に実行した時は、別のIDを指定していたので、Input無しでTransactionが発行されました。
-
>>> start Initiator counterParty: "PartyB" , id: 2 , data: "Bar"
✅ 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
>>> run vaultQuery contractStateType: com.template.states.TemplateState
states:
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 1
data: "Fuga"
linearId:
externalId: null
id: "0332a33a-0c0d-4708-80e4-307b4322c68c"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "758D3F22DE3E48409E9E29113AEB49EB5B5037BDDEE842AF1C24416317F426DC"
index: 0
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 2
data: "Bar"
linearId:
externalId: null
id: "38efca42-3fe1-4c6d-be74-00404715f77e"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "36218C75A486E5A9889F73136939E6121604DD1660A2A732AD9DDC76D9E0F491"
index: 0
statesMetadata:
- ref:
txhash: "758D3F22DE3E48409E9E29113AEB49EB5B5037BDDEE842AF1C24416317F426DC"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T01:52:09.295Z"
consumedTime: null
status: "UNCONSUMED"
notary: "O=Notary, L=London, C=GB"
lockId: null
lockUpdateTime: null
relevancyStatus: "RELEVANT"
constraintInfo:
constraint:
key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
- ref:
txhash: "36218C75A486E5A9889F73136939E6121604DD1660A2A732AD9DDC76D9E0F491"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T01:57:28.737Z"
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: []
▼JDBCの実装
- 公式ドキュメントのJDBC sessionに詳細が記載されています。
- NodeのDatabaseのテーブル定義はNode database tablesに記載されています。
- Schemaを作成すると、ここに追加されていきます。
- この内容を元に、Flow.ktを拡張していきます。
- せっかくなので、NodeのTransactionIDを取得してみます。
- 下記をFlow.ktの末尾に追加します。
object CustomVaultQuery {
@CordaService
class Service(val services: AppServiceHub) : SingletonSerializeAsToken() {
private companion object {
private val log = contextLogger()
}
// 自ノードの全StateからTransactionIDを取得する
fun getTxIDs(): List<String> {
val nativeQuery = """
select
TRANSACTION_ID
from
VAULT_STATES
order by
RECORDED_TIMESTAMP
"""
log.info("SQL to execute: $nativeQuery")
val session = services.jdbcSession()
return session.prepareStatement(nativeQuery).use { prepStatement ->
prepStatement.executeQuery().use { rs ->
val tx_ids: MutableList<String> = mutableListOf()
while (rs.next()) {
val tx_id = rs.getString("TRANSACTION_ID")
tx_ids.add(tx_id)
}
tx_ids
}
}
}
}
}
- 次に、Flow.ktのcallに、このCordaServiceを使用するコードを追記します。
- 特にこのTransactionIDを使うわけでもないので、標準出力するにとどめておきます。
@Suspendable
override fun call() {
// デフォルトのNotaryを指定
val notary = serviceHub.networkMapCache.notaryIdentities[0]
id.equals(id)
val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)
val states = builder {
val condition1 = TemplateSchemaV1.PersistentTemplate::id.equal(id)
val q1 = QueryCriteria.VaultCustomQueryCriteria(condition1)
val criteria = generalCriteria.and(q1)
serviceHub.vaultService.queryBy<TemplateState>(criteria).states
}
// CordaService
val customVaultQueryService = serviceHub.cordaService(CustomVaultQuery.Service::class.java)
val txIDs = customVaultQueryService.getTxIDs()
for (txID in txIDs){
println(txID)
}
▼試しに動かす
- これで、JDBCも使えるようになりました。実際に動かして試してみましょう。
- ノードが起動したままであれば、ターミナルを閉じるか、byeコマンドを実行して、ノードを停止させなす。
$ ./gradlew deployNodes
$ ./build/nodes/runnodes
- ノードの起動まで終わったら、PartyAのノードで実行してみます。
- おや、何も表示されませんね・・・
>>> start Initiator counterParty: "PartyB" , id: 3 , data: "nobkov"
✅ 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
- もう一度実行してみます。
- それらしき文字列が表示されていますね。
- 1回目はTransactionの発行前に検索を行っていたので、IDが無いのは当然でした。
-
run vaultQuery
を見てみると、1回目のTransactionIDと合致していることがわかります。-
ref
のtxhash
がTransactionIDです。
-
>>> start Initiator counterParty: "PartyB" , id: 4 , data: "test"
D52F9B1561A97BC68E6134EA20C8AC11D768C8E91E6F301DF76A18E177B75F3D
✅ 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
>>> run vaultQuery contractStateType: com.template.states.TemplateState
states:
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 3
data: "nobkov"
linearId:
externalId: null
id: "e5cd54ad-c992-43e9-87b3-49918ecbcce7"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "D52F9B1561A97BC68E6134EA20C8AC11D768C8E91E6F301DF76A18E177B75F3D"
index: 0
- state:
data: !<com.template.states.TemplateState>
issuer: "O=PartyA, L=London, C=GB"
counterParty: "O=PartyB, L=New York, C=US"
id: 4
data: "test"
linearId:
externalId: null
id: "d0037f1b-f085-4ce1-8022-9d89041c699c"
participants:
- "O=PartyA, L=London, C=GB"
- "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: "529B88050662CE4BD8216BCA4674FB1652D7312645CD2D1BC797DC78ABB33ED1"
index: 0
statesMetadata:
- ref:
txhash: "D52F9B1561A97BC68E6134EA20C8AC11D768C8E91E6F301DF76A18E177B75F3D"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T02:45:05.994Z"
consumedTime: null
status: "UNCONSUMED"
notary: "O=Notary, L=London, C=GB"
lockId: null
lockUpdateTime: null
relevancyStatus: "RELEVANT"
constraintInfo:
constraint:
key: "aSq9DsNNvGhYxYyqA9wd2eduEAZ5AXWgJTbTEw3G5d2maAq8vtLE4kZHgCs5jcB1N31cx1hpsLeqG2ngSysVHqcXhbNts6SkRWDaV7xNcr6MtcbufGUchxredBb6"
- ref:
txhash: "529B88050662CE4BD8216BCA4674FB1652D7312645CD2D1BC797DC78ABB33ED1"
index: 0
contractStateClassName: "com.template.states.TemplateState"
recordedTime: "2019-09-04T02:48:50.456Z"
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: []
おわりに
今回は、Corda実装には欠かせないStateの検索方法についての記事でした。
特にJDBCでの検索方法は、SQLを記述できることもあり、柔軟な記載ができることや、任意のテーブルに対して射影ができるのが強みです。
#そういえば、ここでUPDATEとか使ったらどうなるんだろ・・・
次回は「Transactionの中身を見てみよう」とか「Transactionを遡ってみよう」とか、Transaction周りについて記事が書ければと思います。
今回はここまでです。
ありがとうございました。