Corda将令牌从帐户转移到另一方(托管在另一方的帐户)

时间:2020-06-26 13:31:10

标签: corda

我们的Corda网络除公证员外还有3个节点。 该图显示了每个节点应该做什么。

Corda nodes

仅在这种情况下,我们遇到麻烦“需要将令牌从帐户持有人转移到乙方”

流代码:


    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用作必需的签名者? 而我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

您的代码中有很多要讨论的地方;让我们从错误开始:

  1. 移动令牌时,唯一需要的签名者是当前持有者;因此,在您的情况下,您应该只传递一个FlowSession,即与PartyA的会话(因为它是令牌的持有者);所以你应该只有:
    CollectSignaturesFlow(initialSignedTrnx, listOf(actualHolderSession))
    
  2. 关于完成交易,必须通过所有参与者的会话。您的案例中所有参与者的并集是令牌的当前持有者(即PartyA)和新的持有者(即PartyB);所以你应该有:
    FinalityFlow(fulySignedTx, listOf(actualHolderSession, partyB))
    
    这就是为什么当两个子流都只传递actualHolderSession时的原因。您收到一个会话丢失(因为最终流程也需要PartyB的会话);并且当您为两者都添加了PartyB会话时,收集签名流程会抱怨您正在传递额外的会话(PartyB不需要对move命令进行签名,只有当前的所有者才可以)。< / li>
  3. 由于您的响应者流程正在处理两项任务(签名和完成);您必须从发起者向响应者发送一些数据(字符串或任何您选择的数据)以表示响应者的角色,因此对于PartyA,您应发送SignerAndFinalizer;而对于PartyB,您发送Finalizer;并且在响应者的开头,您会收到“角色”并采取相应的措施(即如果它是Finalizer,您不会呼叫SignTransactionFlow,而只会叫ReceiveFinalityFlow)。

现在要讨论其​​他主题:

  1. 我不建议一方移动另一方持有的令牌。假设您在帐户中拥有资金,然后我从您的帐户中转移了这笔钱。应该是持有发起移动令牌的令牌的节点。实际上,这就是SDK就绪流程的运行方式。

    如果您查看AbstractMoveTokensFlow,您会发现他们仅依靠ObserverAwareFinalityFlow在本地签署交易(请参见here);为了确认这一点,您可以看到ObserverAwareFinalityFlow内没有CollectSignaturesFlow(即它不要求其他节点签名),而只是在本地签名(请参阅here)。

    这一切的意思是,如果您使用SDK的就绪流程来移动与调用移动流程的节点不同的节点所持有的令牌,那么,您将得到一个错误,因为需要持有者的签名,但是就绪流不会从其他节点收集签名,而只会从称为移动流的节点(不是令牌的持有者)中收集签名。 / p>

    我建议遵循SDK的方法,因为只有令牌的持有者才能移动其令牌。

  2. 帐户库文档中提到的另一件事是不要混合使用帐户和非帐户(即,仅在一个帐户之间移动,或在另一方之间移动);建议您为您的节点创建一个“默认”帐户(例如,创建一个名称与您的节点的X500名称匹配的帐户),请阅读here