为什么我在 Corda 中收到 initiator of CollectSignaturesFlow must pass in exact sessions 错误?

Why do I get initiator of CollectSignaturesFlow must pass in exact sessions error in Corda?

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

问题政策流程:

        // 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.")
    }
}

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

这是 Cash.generateSpend 返回的 cashSigningKeys 的目的。这是 public 键的列表,这些键拥有 Cash.generateSpend 添加到交易生成器的所有现金。

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

时,丢失的正是这个机密身份的会话

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

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