在同一Corda流中创建多个交易有什么风险?

时间:2019-01-16 07:41:08

标签: corda

在Cordapp中,我想更新第二条链作为正常交易的一部分。由于在具有不同参与者的两个单独状态下跟踪数据,因此需要在两个事务中完成。

出于讨论的目的,我们有两个甲方和乙方。A与B发起事务1。在收到事务1时,乙方启动事务2以更新另一个状态。我们如何确保两项交易成功完成?

有两种方法可以解决此问题:

  1. 内联流响应器,为事务2启动subFlow
  2. 使用vaultTrack来响应已提交的事务1,并为事务2启动subFlow

以下是选项1的一些示例代码:

class CustomerIssueFlowResponder(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
    @Suspendable
    override fun call(): SignedTransaction {
        val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                val output = stx.tx.outputs.single().data
                "This must be an CustomerState." using (output is CustomerState)
            }
        }
        // signing transaction 1
        val stx = subFlow(signTransactionFlow)
        val customerState = stx.tx.outputs.single().data as CustomerState
        // initiating transaction 2
        subFlow(CustomerIssueOrUpdateFlow(customerState))

        return stx
    }
}

每种方法的优缺点是什么?

我对选项1的担心是,单个流程中的两个事务不是原子的。两个事务之一可能失败,另一个事务成功,这将使数据处于不一致状态。例如:上方响应者中的subFlow对于事务2可能成功,但是事务1在两次花销问题上经过公证时可能失败。在这种情况下,第二条链将被不正确地更新。

使用vaultTrack会更安全,因为事务1将成功完成,但是没有什么可以保证事务2最终将完成。

1 个答案:

答案 0 :(得分:2)

首先,您说:

  

由于在两个不同的状态下跟踪数据,因此状态不同   参与者这需要分两次完成。

这不一定是正确的。具有不同参与者的两个独立状态可以成为同一事务的一部分。但是,假设您有理由在此处将它们分开(例如隐私)。

从Corda 4开始,该平台不提供多重交易原子性保证。没有内置的方法可以确保仅在提交另一笔交易时才提交给定的交易(但请参见下面的P.S。)。

因此,您的任何选择都不保证多重交易原子性。我仍然相信选项1会更可取,因为您将获得流程将被调用的保证。您担心的是,即使第一个事务失败,响应者也会调用创建第二个事务的流程。可以避免使用waitForLedgerCommit来确保事务1在启动流程以创建第二个事务之前已提交:

class CustomerIssueFlowResponder(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
    @Suspendable
    override fun call(): SignedTransaction {
        val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                val output = stx.tx.outputs.single().data
                "This must be an CustomerState." using (output is CustomerState)
            }
        }
        // signing transaction 1
        val stx = subFlow(signTransactionFlow)
        val customerState = stx.tx.outputs.single().data as CustomerState
        // initiating transaction 2 once transaction 1 is committed
        waitForLedgerCommit(stx.id)
        subFlow(CustomerIssueOrUpdateFlow(customerState))

        return stx
    }
}

P.S。实现多事务原子性的一种可能方法是使用保留,如下所示:

  • 想象一下我们有两个事务:Tx1输出S1,Tx2输出S2
  • 作为Tx1的一部分,负担S1,以便仅当您知道公证人在Tx2上的签名或在一段时间后恢复为原始状态时才可以使用它
  • 作为Tx2的一部分,负担S2,以便仅当您知道公证人对Tx1的签名时才可以使用它,或者在一段时间后恢复为原始状态

但是,想到的一种攻击是FinalityFlow的调用者为Tx1没有在Tx1上分配公证人的签名,从而使他们可以在不放弃Tx1的情况下主张Tx2。如果公证人将所有签名发布到某个公告板上,而不是依靠FinalityFlow的调用者来分发它们,则可以解决此问题。