Finality Flow 不向 extraRecipients 发送交易
Finality Flow not sending transaction to extraRecipients
- Corda 2.0
- JDK1.8.0_162
我正在尝试调试 FinalityFlow 中的不一致行为。不一致,因为 Mock 和 Real 节点中的结果不同。
真实节点流程
我正在尝试通过替代 FinalityFlow 构造函数之一将事务发送到另一个节点:
constructor(transaction: SignedTransaction, extraParticipants: Set<Party>) : this(transaction, extraParticipants, tracker())
我通过 RPC 与我的节点通信。该过程首先通过名称检索其他节点的 Party,例如。 O=PartyA,L=London,C=GB
:
val extraRecipientParties = myExtraRecipientsStringList.map { rpcOps.wellKnownPartyFromX500Name(CordaX500Name.build(X500Principal(it)))!! }
然后,rpcOps 调用负责创建状态的流程:
val flow = rpcOps.startFlow(::CreateStateFlow, other, arguments, extraRecipientParties)
val result = flow.returnValue.getOrThrow()
val newState = result.tx.outRef<MyStateClass>(0)
CreateStateFlow 非常标准:
@StartableByRPC
class CreateStateFlow(
val s: String,
val p: String,
val o: String,
val extraParticipants: List<Party>
) : FlowLogic<SignedTransaction>() {
constructor(s: String, p: String, o: String): this(s, p, o, emptyList())
@Suspendable
override fun call() : SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first()
val newState = MyStateClass(ourIdentity, s, p, o, extraRecipients=extraParticipants)
val command = Command(TripleContract.Create(), listOf(ourIdentity.owningKey))
val outputState = StateAndContract(newState, TripleContract.CONTRACT_REF)
val utx = TransactionBuilder(notary=notary).withItems(
command,
outputState
)
val stx = serviceHub.signInitialTransaction(builder=utx, signingPubKeys=listOf(ourIdentity.owningKey))
if (newState.extraRecipients.isEmpty()) {
return subFlow(FinalityFlow(stx))
}
return subFlow(FinalityFlow(stx, newState.extraRecipients.toSet() ))
}
}
我期望的是,现在,在 extraRecipients 变量中各方拥有的任何节点上,我应该能够通过查询保险库找到 newState
。
确实,当我在 Mock 节点上测试它时是这样,但是当 rpc 调用时不是这样
rpcOps.vaultQueryBy<MyStateClass>().states --> returns an empty list
模拟节点测试
@Test
fun `FinalityFlow used to federate a transaction`(){
val partyAString = node1.info.legalIdentities.first().name.toString()
val aStringX500Name = CordaX500Name.build(X500Principal(partyAString))
val node2FindPartyA = node2.rpcOps.wellKnownPartyFromX500Name(aStringX500Name)!!
assert(node1.info.legalIdentities.contains(node2FindPartyA))
val executingFlow = node2.start(CreateStateFlow("fo", "boo", "bar", listOf(node2FindPartyA)))
val flowResult = executingFlow.getOrThrow()
val stateInNode2 = flowResult.tx.outRef<MyStateClass>(0)
val stateInNode1 = node1.database.transaction {
node1.services.loadState(stateInNode2.ref)
}
assert(stateInNode1.data == stateInNode2.state.data)
编辑:
MyStateClass.kt
data class MyStateClass(
val owner: Party,
val s: String,
val p: String,
val o: String,
val extraRecipients: List<Party>,
val lastEditor: AbstractParty = owner,
override val participants: List<AbstractParty> = listOf(owner),
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState {
object MyStateSchemaV1 : MappedSchema(MyStateClass::class.java, 1, listOf(MyStateEntity::class.java)) {
@Entity
@Table(name = "my-state")
class MyStateEntity(state: MyStateClass) : PersistentState() {
@Column @Lob
var owner: ByteArray = state.owner.owningKey.encoded
@Column
var s: String = state.s
@Column
var p: String = state.p
@Column
var o: String = state.o
@Column @ElementCollection
var extra_recipients: Set<ByteArray> = state.extraRecipients.map { it.owningKey.encoded }.toSet()
@Column @ElementCollection
var participants: Set<ByteArray> = state.participants.map { it.owningKey.encoded }.toSet()
@Column @Lob
var last_editor: ByteArray = state.owner.owningKey.encoded
@Column
var linear_id: String = state.linearId.id.toString()
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(MyStateSchemaV1)
override fun generateMappedObject(schema: MappedSchema): PersistentState = MyStateSchemaV1.MyStateEntity(this)
}
虽然你引入了一个新变量val extraRecipients: List<Party>
,但你的参与者只在拥有者身上,override val participants: List<AbstractParty> = listOf(owner)
因此只有拥有者一方应该拥有保险库中的状态。
FinalityFlow 中的 extraRecipients 不将状态存储在保险库(states storage)中,而是将经过公证的交易副本存储在交易存储中。
loadState
函数的定义是 Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
因为节点 1 在最终流中被添加为交易的额外收件人(将其视为电子邮件的 cc-ed 收件人),当被要求 loadState
时,它能够从交易存储中推断出状态,因为它由输入、命令、输出等组成。所以在这里你已经证明交易是在FinalityFlow
.
在 rpcOps.vaultQueryBy<MyStateClass>().states
上,它实际上是从节点状态库中查询状态 - 而不是事务存储,因此返回了一个空列表。
如果您希望 extraRecipients 存储状态,您需要将它们添加到状态的 participants
字段或使用 observable-states
概念 here.
- Corda 2.0
- JDK1.8.0_162
我正在尝试调试 FinalityFlow 中的不一致行为。不一致,因为 Mock 和 Real 节点中的结果不同。
真实节点流程
我正在尝试通过替代 FinalityFlow 构造函数之一将事务发送到另一个节点:
constructor(transaction: SignedTransaction, extraParticipants: Set<Party>) : this(transaction, extraParticipants, tracker())
我通过 RPC 与我的节点通信。该过程首先通过名称检索其他节点的 Party,例如。 O=PartyA,L=London,C=GB
:
val extraRecipientParties = myExtraRecipientsStringList.map { rpcOps.wellKnownPartyFromX500Name(CordaX500Name.build(X500Principal(it)))!! }
然后,rpcOps 调用负责创建状态的流程:
val flow = rpcOps.startFlow(::CreateStateFlow, other, arguments, extraRecipientParties)
val result = flow.returnValue.getOrThrow()
val newState = result.tx.outRef<MyStateClass>(0)
CreateStateFlow 非常标准:
@StartableByRPC
class CreateStateFlow(
val s: String,
val p: String,
val o: String,
val extraParticipants: List<Party>
) : FlowLogic<SignedTransaction>() {
constructor(s: String, p: String, o: String): this(s, p, o, emptyList())
@Suspendable
override fun call() : SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first()
val newState = MyStateClass(ourIdentity, s, p, o, extraRecipients=extraParticipants)
val command = Command(TripleContract.Create(), listOf(ourIdentity.owningKey))
val outputState = StateAndContract(newState, TripleContract.CONTRACT_REF)
val utx = TransactionBuilder(notary=notary).withItems(
command,
outputState
)
val stx = serviceHub.signInitialTransaction(builder=utx, signingPubKeys=listOf(ourIdentity.owningKey))
if (newState.extraRecipients.isEmpty()) {
return subFlow(FinalityFlow(stx))
}
return subFlow(FinalityFlow(stx, newState.extraRecipients.toSet() ))
}
}
我期望的是,现在,在 extraRecipients 变量中各方拥有的任何节点上,我应该能够通过查询保险库找到 newState
。
确实,当我在 Mock 节点上测试它时是这样,但是当 rpc 调用时不是这样
rpcOps.vaultQueryBy<MyStateClass>().states --> returns an empty list
模拟节点测试
@Test
fun `FinalityFlow used to federate a transaction`(){
val partyAString = node1.info.legalIdentities.first().name.toString()
val aStringX500Name = CordaX500Name.build(X500Principal(partyAString))
val node2FindPartyA = node2.rpcOps.wellKnownPartyFromX500Name(aStringX500Name)!!
assert(node1.info.legalIdentities.contains(node2FindPartyA))
val executingFlow = node2.start(CreateStateFlow("fo", "boo", "bar", listOf(node2FindPartyA)))
val flowResult = executingFlow.getOrThrow()
val stateInNode2 = flowResult.tx.outRef<MyStateClass>(0)
val stateInNode1 = node1.database.transaction {
node1.services.loadState(stateInNode2.ref)
}
assert(stateInNode1.data == stateInNode2.state.data)
编辑: MyStateClass.kt
data class MyStateClass(
val owner: Party,
val s: String,
val p: String,
val o: String,
val extraRecipients: List<Party>,
val lastEditor: AbstractParty = owner,
override val participants: List<AbstractParty> = listOf(owner),
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState {
object MyStateSchemaV1 : MappedSchema(MyStateClass::class.java, 1, listOf(MyStateEntity::class.java)) {
@Entity
@Table(name = "my-state")
class MyStateEntity(state: MyStateClass) : PersistentState() {
@Column @Lob
var owner: ByteArray = state.owner.owningKey.encoded
@Column
var s: String = state.s
@Column
var p: String = state.p
@Column
var o: String = state.o
@Column @ElementCollection
var extra_recipients: Set<ByteArray> = state.extraRecipients.map { it.owningKey.encoded }.toSet()
@Column @ElementCollection
var participants: Set<ByteArray> = state.participants.map { it.owningKey.encoded }.toSet()
@Column @Lob
var last_editor: ByteArray = state.owner.owningKey.encoded
@Column
var linear_id: String = state.linearId.id.toString()
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(MyStateSchemaV1)
override fun generateMappedObject(schema: MappedSchema): PersistentState = MyStateSchemaV1.MyStateEntity(this)
}
虽然你引入了一个新变量val extraRecipients: List<Party>
,但你的参与者只在拥有者身上,override val participants: List<AbstractParty> = listOf(owner)
因此只有拥有者一方应该拥有保险库中的状态。
FinalityFlow 中的 extraRecipients 不将状态存储在保险库(states storage)中,而是将经过公证的交易副本存储在交易存储中。
loadState
函数的定义是 Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
因为节点 1 在最终流中被添加为交易的额外收件人(将其视为电子邮件的 cc-ed 收件人),当被要求 loadState
时,它能够从交易存储中推断出状态,因为它由输入、命令、输出等组成。所以在这里你已经证明交易是在FinalityFlow
.
在 rpcOps.vaultQueryBy<MyStateClass>().states
上,它实际上是从节点状态库中查询状态 - 而不是事务存储,因此返回了一个空列表。
如果您希望 extraRecipients 存储状态,您需要将它们添加到状态的 participants
字段或使用 observable-states
概念 here.