首页 > 解决方案 > 为什么我得到 CollectSignaturesFlow 的发起者必须在 Corda 中传递确切的会话错误?

问题描述

我正在制作一个涉及两方的 CorDapp——客户和承销商。我有两个主要流程,一个是 IssuePolicy,另一个是 PayoutPolicy。当我运行每个流程一次时,没有问题。当我再次运行 IssuePolicy 时,我收到错误消息:The Initiator of CollectSignaturesFlow must pass in exactly the sessions required to sign the transaction.我已经搜索了 Stack Overflow 并找到了这篇文章:CollectSignaturesFlow 中的 Flow Exception但我相信我已经与一方签署并与另一方启动了流程,所以我不确定这是否解决方案适用于我。任何帮助,将不胜感激!

问题政策流程:

        // Step 3. Building.
        progressTracker.currentStep = BUILDING
        val notary = serviceHub.networkMapCache.notaryIdentities[0]
        val utx = TransactionBuilder(notary = notary)
                .addOutputState(policy, INSUREFLIGHT_CONTRACT_ID)
                .addCommand(InsureFlightContract.Commands.Issue(), policy.participants.map { it.owningKey })
                .setTimeWindow(serviceHub.clock.instant(), 30.seconds)

        // Stage 4. Get some cash from the vault and add a spend to our transaction builder.
        // We pay cash to the underwriter's policy key.
        val (_, cashSigningKeys) = Cash.generateSpend(serviceHub, utx, premium, underwriter)
        check(cashSigningKeys == cashSigningKeys){
            throw FlowException("")
        }
        // Step 5. Sign the transaction.
        progressTracker.currentStep = SIGNING
        val ptx = serviceHub.signInitialTransaction(utx, policy.client.owningKey)

        // Step 6. Get the counter-party signature.
        progressTracker.currentStep = COLLECTING
        val otherpartySession = initiateFlow(underwriter)
        val stx = subFlow(CollectSignaturesFlow(
                ptx,
                listOf(otherpartySession),
                COLLECTING.childProgressTracker())
        )

        // Step 7. Finalize the transaction.
        progressTracker.currentStep = FINALIZING
        return subFlow(FinalityFlow(stx, FINALIZING.childProgressTracker()))
    }
}

// Allows counterparty to respond.
@InitiatedBy(Initiator::class)
class IssuePolicyResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
    val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
        override fun checkTransaction(stx: SignedTransaction) = requireThat {
        }
    }

    subFlow(signTransactionFlow)
}
}
}

政策状态定义:

//Policy Class, includes premium, claim, client, underwriter, flight, and policyID
data class Policy(val premium: Amount<Currency>,
              val claim: Amount<Currency>,
              val client: Party,
              val underwriter: Party,
              val flight: String,
              override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState {

//Get clients and underwriters
override val participants: List<Party> get() = listOf(client, underwriter)

//Functions to update policy parameters
fun payPremium(amountToPay: Amount<Currency>) = copy(premium = premium + amountToPay)
fun payClaim(amountToPay: Amount<Currency>) = copy(claim = claim + amountToPay)
fun withNewClient(newClient: Party) = copy(client = newClient)
fun withNewUnderwriter(newUnderwriter: Party) = copy(underwriter = newUnderwriter)
fun withNewFlight(newFlight: String) = copy(flight = newFlight)

//Provides response
override fun toString(): String {
    val clientString = (client as? Party)?.name?.organisation ?: client.owningKey.toBase58String()
    val underwriterString = (underwriter as? Party)?.name?.organisation ?: underwriter.owningKey.toBase58String()
    return "Policy($linearId): $clientString has paid a premium of $$premium for flight $flight, underwritten by $underwriterString with" +
            "a claim amount of $$claim."
}
}

合同中的命令:

interface Commands : CommandData {
    class Issue : TypeOnlyCommandData(), Commands
    class Settle : TypeOnlyCommandData(), Commands
}

override fun verify(tx: LedgerTransaction): Unit {
    val command = tx.commands.requireSingleCommand<Commands>()
    val setOfSigners = command.signers.toSet()
    when (command.value) {
        is Commands.Issue -> verifyIssue(tx, setOfSigners)
        is Commands.Settle -> verifySettle(tx, setOfSigners)
        else -> throw IllegalArgumentException("Unrecognized command. You can only issue or settle.")
    }
}

标签: corda

解决方案


并非您节点保险库中的所有现金都将归您节点的主公钥所有。这是因为在Cash.generateSpend生成更改输出时,出于隐私原因,此更改被分配给一个新的机密身份,而不是您节点的主要身份。

这就是cashSigningKeys返回的目的Cash.generateSpend。它是一个公钥列表,拥有由Cash.generateSpend.

我猜你第一次运行IssuePolicy/PayoutPolicy时,它会产生一些由新的机密身份拥有的现金。您永远不会使用新机密身份的密钥签署交易。实际上,当您调用 时,缺少的是这个机密身份的会话CollectSignaturesFlow

当然,用这个机密身份创建会话是没有意义的,因为它实际上对应于运行流的节点。相反,您需要获取cashSigningKeys返回的 by Cash.generateSpend,并在调用之前使用这些签名交易CollectSignaturesFlow,方法是调用:

val ptx = serviceHub.signInitialTransaction(
    utx, 
    cashSigningKeys + policy.client.owningKey)

推荐阅读