corda - 为什么我得到 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.")
}
}
解决方案
并非您节点保险库中的所有现金都将归您节点的主公钥所有。这是因为在Cash.generateSpend
生成更改输出时,出于隐私原因,此更改被分配给一个新的机密身份,而不是您节点的主要身份。
这就是cashSigningKeys
返回的目的Cash.generateSpend
。它是一个公钥列表,拥有由Cash.generateSpend
.
我猜你第一次运行IssuePolicy
/PayoutPolicy
时,它会产生一些由新的机密身份拥有的现金。您永远不会使用新机密身份的密钥签署交易。实际上,当您调用 时,缺少的是这个机密身份的会话CollectSignaturesFlow
。
当然,用这个机密身份创建会话是没有意义的,因为它实际上对应于运行流的节点。相反,您需要获取cashSigningKeys
返回的 by Cash.generateSpend
,并在调用之前使用这些签名交易CollectSignaturesFlow
,方法是调用:
val ptx = serviceHub.signInitialTransaction(
utx,
cashSigningKeys + policy.client.owningKey)