Ledger Sync от Corda не синхронизирует состояния хранилища при определенных обстоятельствах - PullRequest
0 голосов
/ 17 января 2019

В настоящее время мы работаем над интеграцией службы синхронизации книги в нашем Cordapp: https://github.com/corda/corda-solutions/tree/master/bn-apps/ledger-sync

В ходе наших собственных испытаний мы обнаружили, что при определенных обстоятельствах бухгалтерская книга не успешно синхронизируется / восстанавливается после сбоя.

Наш тест выполняет следующее:

  • Узел A и B осуществляют транзакции друг с другом, создавая состояние S.
  • Узел B аварийно завершает работу и восстанавливается до состояния, в котором он не знает S.
  • Узел A создает новую транзакцию, которая потребляет состояние S
  • Узел B использует службу синхронизации главной книги для восстановления всех состояний.

В фоновом режиме происходит следующее: Когда узел A создает Tx, который потребляет состояние S, узел B также получит старый Tx, создавший состояние S в качестве зависимости. С этого момента Tx записывается в базу данных узла B и может быть получен путем вызова serviceHub.validatedTransactions.getTransaction(txId).

Однако запрос хранилища для состояний CONSUMED или ALL не вернет старое состояние S. Запуск синхронизации по регистру сообщит, что узел не синхронизирован, сообщив, что транзакция, которая создала состояние S, отсутствует.

Вызов ремонта не приведет к успешному восстановлению, а последовательные прогоны RequestLedgersSyncFlow будут сообщать о пропущенных транзакциях.

Я не уверен, что этот вариант использования фактически поддерживается (создание Txs, когда регистр не синхронизирован), но я думаю, что если это не поддерживаемый вариант использования, трудно убедиться, что узлы не взаимодействуют с каждым другой, когда один из узлов не синхронизирован.

Надеюсь, проблема ясна, в противном случае я также могу подготовить и предоставить тест для нее.

Обновление: По запросу я создал форк репозитория Corda Solutions и добавил тест, который демонстрирует ошибку: https://github.com/marioschlipf/corda-solutions/commit/fe1ab5917c971fcf9732bf8af7d0f2c1800b5e37

1 Ответ

0 голосов
/ 21 января 2019

Я воссоздал сценарий с четырьмя узлами, работающими Служба синхронизации книги , созданная из мастера (последний коммит 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, содержащий этот поток, развернут на трех узлах (Алиса A, Боб B, Чарли C). Нотариальный нотариус (N) используется.

Рассмотрите следующие шаги для симуляции сбоя и восстановления.

  1. Запуск A, B, C и N с использованием H2 в качестве базы данных
  2. Как A, вызвать net.corda.businessnetworks.ledgersync.BogusFlow, нацеливание O=Bob Ltd., L=London, C=GB
  3. Завершить работу узла A и уничтожить базу данных, т.е. rm persistence.mv.db
  4. Как B, запустите vaultQuery для contractStateType net.corda.businessnetworks.ledgersync.BogusState, чтобы проверить, B знает о неиспользованном состоянии после 2 . Вывод должен содержать linearId. Запишите этот идентификатор.
  5. Как B, начать поток с C, используя linearId, полученный в 4 в качестве прекурсора. То есть 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 и подтвердите, что с участниками B и C (т.е. "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}, указывая, что A не синхронизирован с B.
  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), чтобы получить содержимое и убедиться, что состояние было воссоздано.

Охватывает ли это сценарий, который вы описываете?

...