为什么我得到CollectSignaturesFlow的发起者必须在Corda中传递准确的会话错误?

时间:2018-07-12 13:49:46

标签: corda

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

问题政策流程:

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

1 个答案:

答案 0 :(得分:3)

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

这是cashSigningKeys返回的Cash.generateSpend的目的。这是拥有Cash.generateSpend添加到交易生成器中的所有现金的公共密钥的列表。

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

当然,使用此机密身份创建会话是没有意义的,因为它实际上对应于运行流的节点。相反,您需要使用cashSigningKeys返回的Cash.generateSpend,并在调用CollectSignaturesFlow之前通过以下方法用它们对事务进行签名:

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