Во-первых, вы говорите:
Поскольку данные отслеживаются в двух отдельных состояниях с разными
Участникам это необходимо будет сделать за две транзакции.
Это не обязательно так. Два отдельных состояния с разными участниками могут быть частью одной транзакции. Однако предположим, что у вас есть причина разделять их здесь (например, конфиденциальность).
Начиная с 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, что позволяет им требовать Tx2, не отказываясь от Tx1. Это было бы решено, если бы нотариус опубликовал все свои подписи на какой-либо доске объявлений вместо того, чтобы полагаться на вызывающего абонента FinalityFlow
для их распространения.