我们目前正致力于在我们的 Cordapp 中集成 Ledger Sync Service:https://github.com/corda/corda-solutions/tree/master/bn-apps/ledger-sync
在我们自己的测试中,我们体验到在某些情况下,账本在崩溃后没有成功同步/修复。
我们的测试执行以下操作:
A
和 B
相互交易,创建状态 S
. B
崩溃并恢复到它不知道的状态 S
. A
创建一个使用状态 S
的新事务B
使用账本同步服务恢复所有状态。 在后台,会发生以下情况:当节点
A
创建使用状态 S
的 Tx , 节点 B
还将收到创建状态 S
的旧 Tx作为依赖。从那时起,Tx 记录在节点 B
的数据库中。并且可以通过调用 serviceHub.validatedTransactions.getTransaction(txId)
来检索.但是,在保管库中查询
CONSUMED
或 ALL
states 不会返回旧状态 S
.运行账本同步会报告节点不同步,说创建状态的事务S
不见了。调用修复将无法成功修复,连续运行
RequestLedgersSyncFlow
将继续报告丢失的交易。我不确定这个用例是否真的受支持(在分类帐不同步时创建 Txs),但我认为如果它不是受支持的用例,则很难确保节点之间没有相互交易的节点不同步。
我希望问题清楚,否则我也可以为此准备并提供测试。
更新:
根据要求,我在这里创建了 Corda Solutions 存储库的一个分支,并添加了一个显示错误的测试:https://github.com/marioschlipf/corda-solutions/commit/fe1ab5917c971fcf9732bf8af7d0f2c1800b5e37
最佳答案
我已经重新创建了四个节点运行从 master 构建的 Ledger Sync Service 的场景(最近的提交 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
)。考虑以下步骤来模拟故障和恢复。
A
, B
, C
和 N
使用 H2 作为数据库 A
, 调用 net.corda.businessnetworks.ledgersync.BogusFlow
, 定位 O=Bob Ltd., L=London, C=GB
A
并销毁数据库,即 rm persistence.mv.db
B
,为 contractStateType net.corda.businessnetworks.ledgersync.BogusState
运行 vaultQuery验证 B
知道 2 之后的未使用状态。输出应包含 linearId
.记下这个 ID。 B
, 用 C
开始一个流程, 利用 linearId
在 4 中获得作为前体。 IE。 flow start net.corda.businessnetworks.ledgersync.BogusFlow them: "O=Charlie SARL, L=Paris, C=FR", precursor: "2429c289-0ccb-4adb-9714-32ee3d0d7f12"
.请注意,在生产用例中,您的合约代码可能会首先禁止执行此交易,因为 A
还没有签名。 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" ]
)。 A
,让节点备份,创建一个新的 H2 数据库。 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
不同步. 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=[])}
)。 A
, 运行 TransactionRecoveryFlow
, 传入 9 的结果.例如。 connection.proxy.startFlow(::TransactionRecoveryFlow, report)
在哪里 report
是上一步的结果。 A
, 验证重新运行 EvaluateLedgerConsistencyFlow
这将返回结果 {O=Bob Ltd., L=London, C=GB=true, O=Charlie SARL, L=Paris, C=FR=true}
,表示差异已解决。 A
,运行保险库查询(即 VaultQueryCriteria(status = ALL), PageSpecification(), Sort(emptyList()), BogusState::class.java
)以检索内容并验证已重新创建状态。 这是否涵盖了您所描述的场景?
关于Corda 的 Ledger Sync 在某些情况下不同步保管库状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54231839/