【问题标题】:Corda's Ledger Sync not syncing vault states in certain circumstancesCorda 的 Ledger Sync 在某些情况下不同步保管库状态
【发布时间】:2019-06-11 09:53:07
【问题描述】:

我们目前正致力于在我们的 Cordapp 中集成 Ledger Sync Service:https://github.com/corda/corda-solutions/tree/master/bn-apps/ledger-sync

在我们自己的测试中,我们发现在某些情况下,账本在崩溃后没有成功同步/修复。

我们的测试执行以下操作:

  • 节点AB相互交易,创建状态S
  • 节点B 崩溃并恢复到不知道S 的状态。
  • 节点A 创建一个新事务,使用状态S
  • 节点B 使用账本同步服务恢复所有状态。

在后台,会发生以下情况:当节点A 创建消费状态S 的Tx 时,节点B 还将接收创建状态S 的旧Tx 作为依赖项。从那时起,Tx 记录在节点B 的数据库中,可以通过调用serviceHub.validatedTransactions.getTransaction(txId) 来检索。

但是,查询库中的 CONSUMEDALL 状态不会返回旧状态 S。运行账本同步会报节点不同步,说创建状态S的事务丢失了。

调用修复将无法成功修复,连续运行RequestLedgersSyncFlow 将不断报告丢失的事务。

我不确定这个用例是否真的受支持(在分类帐不同步时创建 Txs),但我认为如果它不是受支持的用例,则很难确保节点不与每个节点进行交易其他节点不同步时。

希望问题清楚,否则我也可以准备并提供测试。

更新: 根据要求,我在这里创建了 Corda Solutions 存储库的一个分支,并添加了一个显示错误的测试:https://github.com/marioschlipf/corda-solutions/commit/fe1ab5917c971fcf9732bf8af7d0f2c1800b5e37

【问题讨论】:

  • 如果可能,请添加测试,您能否提供用于查询所有状态的 sn-p。
  • 您能否提供有关如何模拟节点崩溃的详细信息? github.com/corda/corda-solutions/blob/… 将跳过节点仍然可用的任何事务(无论是处于缓存状态还是在保险库中)。
  • @Adrian 我添加了一个测试,看我原来的问题
  • @mritz_p 我们正在备份 h2 数据库并在节点关闭的情况下恢复它。我认为这是一个比摆弄 SQL 更强大的测试。但是,我设法通过 R3 在其设置中使用的测试设置展示了该错误。请参阅我原来的问题。编辑:我已经看到你提到的那行代码,删除这个过滤器不会改变任何东西,已经测试过了。
  • @mario.schlipf 不应该是 l 中的断言。 186 是assertEquals(0, ledgerSyncResult2[node1.fromNetwork().identity()]!!.missingAtRequester.size)。因为你之前运行过恢复?

标签: corda


【解决方案1】:

我重新创建了一个场景,其中四个节点运行 Ledger Sync Service,这些节点是从 master 构建的(最近一次提交 839dfb8772c3b08447183a84e336a527a0f3975b)。我已通过以下方式修改BogusFlow 以允许使用输入状态:

/**
 * A trivial flow that is merely used to illustrate synchronisation by persisting meaningless transactions in
 * participant's vaults
 */
@InitiatingFlow
@StartableByRPC
class BogusFlow(
        private val them: Party,
        private val precursor: UniqueIdentifier? = null
) : FlowLogic<SignedTransaction>() {

    @Suspendable
    override fun call(): SignedTransaction {
        val notary = serviceHub.networkMapCache.notaryIdentities.first()

        val cmd = Command(BogusContract.Commands.Bogus(), listOf(them.owningKey))

        val builder = TransactionBuilder(notary)

        precursor?.let {
            val result = serviceHub.vaultService.queryBy(BogusState::class.java, LinearStateQueryCriteria(linearId = listOf(it)))
            val inputState = result.states.single()
            builder.addInputState(inputState)
        }

        builder.addOutputState(BogusState(ourIdentity, them), BOGUS_CONTRACT_ID)
                .addCommand(cmd).apply {
                    verify(serviceHub)
                }

        val partiallySigned = serviceHub.signInitialTransaction(builder)

        val session = initiateFlow(them)

        val fullySigned = subFlow(CollectSignaturesFlow(partiallySigned, setOf(session)))

        return subFlow(FinalityFlow(fullySigned))
    }
}

包含此流的 CorDapp 部署到三个节点(Alice A、Bob B、Charlie C)。使用了非验证公证人 (N)。

考虑以下步骤来模拟故障和恢复。

  1. 使用 H2 作为数据库启动 ABCN
  2. 作为A,调用net.corda.businessnetworks.ledgersync.BogusFlow,定位O=Bob Ltd., L=London, C=GB
  3. 关闭节点A并销毁数据库,即rm persistence.mv.db
  4. 作为B,为contractStateType net.corda.businessnetworks.ledgersync.BogusState 运行vaultQuery 以验证B 知道2 之后的未使用状态。输出应包含linearId。记下此 ID。
  5. 作为B,以C 开始一个流,利用4 中获得的linearId 作为前导。 IE。 flow start net.corda.businessnetworks.ledgersync.BogusFlow them: "O=Charlie SARL, L=Paris, C=FR", precursor: "2429c289-0ccb-4adb-9714-32ee3d0d7f12"。请注意,在生产用例中,您的合约代码可能会首先禁止执行此交易,因为 A 尚未签署它。
  6. 作为B,运行vaultQuery contractStateType: net.corda.businessnetworks.ledgersync.BogusState 并验证参与者BC 存在未消费状态(即"participants" : [ "O=Bob Ltd., L=London, C=GB", "O=Charlie SARL, L=Paris, C=FR" ])。
  7. A 的身份恢复节点,创建新的 H2 数据库。
  8. 作为A,启动EvaluateLedgerConsistencyFlow(即connection.proxy.startFlow(::EvaluateLedgerConsistencyFlow, listOf(alice, bob, charlie)))。这应该返回 {O=Bob Ltd., L=London, C=GB=false, O=Charlie SARL, L=Paris, C=FR=true},表明 AB 不同步。
  9. 作为A,运行RequestLedgersSyncFlow(即connection.proxy.startFlow(::RequestLedgersSyncFlow, listOf(alice, bob, charlie)))。这将返回丢失交易的摘要(例如{O=Bob Ltd., L=London, C=GB=LedgerSyncFindings(missingAtRequester=[BAA58E9E9E2025181F00459FCE8B0D035705A38D1068A0F4C4BAB53F3F56FB40], missingAtRequestee=[]), O=Charlie SARL, L=Paris, C=FR=LedgerSyncFindings(missingAtRequester=[], missingAtRequestee=[])})。
  10. 作为A,运行TransactionRecoveryFlow传入9的结果。例如。 connection.proxy.startFlow(::TransactionRecoveryFlow, report) 其中report 是上一步的结果。
  11. 作为A,验证重新运行EvaluateLedgerConsistencyFlow将返回结果{O=Bob Ltd., L=London, C=GB=true, O=Charlie SARL, L=Paris, C=FR=true},表明差异已解决。
  12. 为了进一步验证,作为A,运行保险库查询(即VaultQueryCriteria(status = ALL), PageSpecification(), Sort(emptyList()), BogusState::class.java)以检索内容并验证状态是否已重新创建。

这是否涵盖了您所描述的场景?

【讨论】:

  • 感谢您抽出宝贵时间调查此问题!你描述的场景有点不同。在我们的场景中,第二个事务也在节点AB 之间,这导致A 在签署消费事务时检索发出事务。关于您的第 5 点:您说得对,在生产用例中,节点 A 可能 需要签署第二个 tx。但是,在我们的用例中,这不是必需的,B 可以在没有A 签署交易的情况下使用。您是否测试过部署我发布的确切合同?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
相关资源
最近更新 更多