【问题标题】:Finality Flow not sending transaction to extraRecipientsFinality Flow 未向 extraRecipients 发送交易
【发布时间】:2018-03-24 22:35:02
【问题描述】:
  • Corda 2.0
  • JDK 1.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)

}

【问题讨论】:

  • 介意发布您的州级代码吗?
  • 给你@Adrian,它实现了可查询和线性状态

标签: corda


【解决方案1】:

虽然您引入了一个新变量val extraRecipients: List&lt;Party&gt;,但您的参与者仅在所有者上,override val participants: List&lt;AbstractParty&gt; = listOf(owner) 因此只有所有者方才应该拥有保管库中的状态。

FinalityFlow 中的 extraRecipients 不会将状态存储在保险库(状态存储)中,但它们会将经过公证的交易的副本存储在交易存储中。

loadState 函数的定义是Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState]. 因为节点 1 在最终流程中被添加为交易的额外收件人(将其视为电子邮件的抄送收件人),当被要求 loadState ,它能够从交易存储中推断出状态,因为它由输入、命令、输出等组成。所以在这里你已经证明交易是在FinalityFlow期间发送给其他方的。

rpcOps.vaultQueryBy&lt;MyStateClass&gt;().states 上,它实际上是从节点状态库查询状态 - 不是事务存储,因此返回一个空列表。

如果您希望 extraRecipients 存储状态,则需要将它们添加到状态的 participants 字段中或使用 observable-states 概念 here

【讨论】:

  • 我认为所有状态最终都在保管库中,事务存储引用了这些状态。我知道从这里做什么。感谢您的回答,这真的很有帮助,而且措辞得当!
猜你喜欢
  • 2023-02-14
  • 2020-03-19
  • 2022-11-13
  • 2021-10-22
  • 2018-07-18
  • 1970-01-01
  • 2014-05-30
  • 2015-10-13
  • 2018-04-22
相关资源
最近更新 更多