我们的Corda网络除公证员外还有3个节点。 该图显示了每个节点应该做什么。
仅在这种情况下,我们遇到麻烦“需要将令牌从帐户持有人转移到乙方”
流代码:
class TransferETokenDiffNodeFlow(val actualHolder: AbstractParty, val newHolder: AbstractParty, val numEtokens: Double, val observables: MutableList = mutableListOf()) : FlowLogic() { private fun notary() = serviceHub.networkMapCache.notaryIdentities.first() @Suspendable override fun call(): SignedTransaction { progressTracker.currentStep = INITIALIZING val txBuilder = TransactionBuilder(notary()) val actualHolderStateRef = accountService.accountInfo(actualHolder.owningKey) ?: throw AccountNotFoundException("Account not found exception.") val actualHolderInfo = actualHolderStateRef.state.data val actualHolderSession = initiateFlow(actualHolderInfo.host) actualHolderSession.send(numEtokens) actualHolderSession.send(actualHolder) actualHolderSession.send(newHolder) val inputs = subFlow(ReceiveStateAndRefFlow(actualHolderSession)) val tokens: List = actualHolderSession.receive>().unwrap { it -> it} progressTracker.currentStep = BUILDING addMoveTokens(txBuilder, inputs, tokens) progressTracker.currentStep = SIGNING val initialSignedTrnx = serviceHub.signInitialTransaction(txBuilder) progressTracker.currentStep = GATHERING_SIGS val fulySignedTx= subFlow(CollectSignaturesFlow(initialSignedTrnx, listOf(actualHolderSession))) progressTracker.currentStep = FINALISING_CREATE val stx = subFlow(FinalityFlow(fulySignedTx, listOf(actualHolderSession))) progressTracker.currentStep = FINALISING val statesTx = stx.tx.outRefsOfType() statesTx.forEach { state -> observables.forEach { observable -> subFlow(ShareStateAndSyncAccounts(state, observable)) } } return stx } } //ResponderFlow code: class TransferETokenDiffNodeFlowResponder(val counterpartySession: FlowSession) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { val numEtokens = counterpartySession.receive().unwrap { it } val actualHolder = counterpartySession.receive().unwrap { it } val newHolder = counterpartySession.receive().unwrap { it } val partyAndAmount = PartyAndAmount(newHolder, numEtokens of EnergyTokenType.getInstance("ENERGY")) val actualHolderStateRef = accountService.accountInfo(actualHolder.owningKey) ?: throw AccountNotFoundException("Account not found exception.") val actualHolderInfo = actualHolderStateRef.state.data val criteria = QueryCriteria.VaultQueryCriteria(externalIds = listOf(actualHolderInfo.identifier.id), status = Vault.StateStatus.UNCONSUMED) val selector = DatabaseTokenSelection(serviceHub) val (inputs, outputs) = selector.generateMove(listOf(partyAndAmount).toPairs(), actualHolder, TokenQueryBy(queryCriteria = criteria), runId.uuid) subFlow(SendStateAndRefFlow(counterpartySession, inputs)) counterpartySession.send(outputs) subFlow(object : SignTransactionFlow(counterpartySession) { @Throws(FlowException::class) override fun checkTransaction(stx: SignedTransaction) { } }) return subFlow(ReceiveFinalityFlow(counterpartySession)) } }
我们需要在C方执行该流程,实际的Holder是帐户所有者,而newHolder是B方。
使用此代码将返回错误: net.corda.core.CordaRuntimeException:java.lang.IllegalArgumentException:没有为以下事务参与者提供流会话:[O =乙方,L =库里蒂巴,C = BR]
但是,如果我更改代码并添加了乙方会话,它将返回错误: java.lang.IllegalArgumentException:CollectSignaturesFlow的发起者必须完全传递签署交易所需的会话。
问题是,为什么addMoveTokens不将newHolder用作必需的签名者? 而我该如何解决这个问题?
答案 0 :(得分:1)
您的代码中有很多要讨论的地方;让我们从错误开始:
FlowSession
,即与PartyA
的会话(因为它是令牌的持有者);所以你应该只有:
CollectSignaturesFlow(initialSignedTrnx, listOf(actualHolderSession))
PartyA
)和新的持有者(即PartyB
);所以你应该有:
FinalityFlow(fulySignedTx, listOf(actualHolderSession, partyB))
这就是为什么当两个子流都只传递actualHolderSession
时的原因。您收到一个会话丢失(因为最终流程也需要PartyB
的会话);并且当您为两者都添加了PartyB
会话时,收集签名流程会抱怨您正在传递额外的会话(PartyB
不需要对move命令进行签名,只有当前的所有者才可以)。< / li>
PartyA
,您应发送SignerAndFinalizer
;而对于PartyB
,您发送Finalizer
;并且在响应者的开头,您会收到“角色”并采取相应的措施(即如果它是Finalizer
,您不会呼叫SignTransactionFlow
,而只会叫ReceiveFinalityFlow
)。现在要讨论其他主题:
我不建议一方移动另一方持有的令牌。假设您在帐户中拥有资金,然后我从您的帐户中转移了这笔钱。应该是持有发起移动令牌的令牌的节点。实际上,这就是SDK就绪流程的运行方式。
如果您查看AbstractMoveTokensFlow
,您会发现他们仅依靠ObserverAwareFinalityFlow
在本地签署交易(请参见here);为了确认这一点,您可以看到ObserverAwareFinalityFlow
内没有CollectSignaturesFlow
(即它不要求其他节点签名),而只是在本地签名(请参阅here)。
这一切的意思是,如果您使用SDK的就绪流程来移动与调用移动流程的节点不同的节点所持有的令牌,那么,您将得到一个错误,因为需要持有者的签名,但是就绪流不会从其他节点收集签名,而只会从称为移动流的节点(不是令牌的持有者)中收集签名。 / p>
我建议遵循SDK的方法,因为只有令牌的持有者才能移动其令牌。
帐户库文档中提到的另一件事是不要混合使用帐户和非帐户(即,仅在一个帐户之间移动,或在另一方之间移动);建议您为您的节点创建一个“默认”帐户(例如,创建一个名称与您的节点的X500名称匹配的帐户),请阅读here。