摘要:在 Corda 中的所有资产默认是只有交易的参与方所拥有的,那么在 Corda 中可以将一笔交易信息分享给未参与该笔交易的节点么?答案是可以的,本文就在 Corda 中完成了一个简易版的广播实现。
Corda 是一个基于联盟链的分布式账本技术,在 Corda 中,“隐私”是其核心的设计。因此,如果 A 向 B 买了一辆车,那么在链上只有 A (Party) 和 B(Party) 能够看到在这辆车 (State) 之间发生的交易,其他节点都是不知道这笔交易信息的。但是,在现实生活中,很多情况下,都会有监管机构的存在,我们所编写的 Dapp 还需要满足监管的要求。例如,在 A 向 B 买了一辆车这个例子中,就还需要向车管所进行登记,以让车管所知道这辆车的信息及其所属情况,以便将来关于这辆车发生的任何问题车管所都能知道它的责任人是谁。因此,在这个场景中我们还需要将 A 向 B 买车的这笔交易信息共享给第三方,也就是车管所。
Ledger(如图为 Corda 的账本模型,A 节点 和 B 节点 之间的交易信息,只会在 A 和 B 各自的账本中是可见的。)
在默认情况下,Corda 是不会将交易信息共享给与该笔交易无关的节点的,这也是 Corda 和其他 DLT 技术相比很大的特点。但这并不意味着 Corda 就没有能力实现这件事了,实际上,Corda 可以在网络中的节点之间发送任何内容。Corda 的这个能力也在其官方文档中有所提及。本文就在此基础上实现 Corda 中的广播。
首先,还是先以上面提到的场景为例,当 A 向 B 买了一辆车后,将该交易信息共享给车管所。我们需要在 BuyVehicleFlow
的最后,调用 subFlow
将交易信息共享给车管所。
object BuyVehicleFlow {
@InitiatingFlow
@StartableByRPC
class Initiator(
private val vin: String
) {
@Suspendable
override fun call(): SignedTransaction {
...... // 省略无关逻辑
val finalTx = subFlow(FinalityFlow(allSignedTransaction))
val vehicleAdministrator = serviceHub.identityService.partiesFromName("VehicleAdministrator", false).single()
subFlow(ReportToVehicleAdministratorFlow(vehicleAdministrator, finalTx))
return finalTx
}
}
这里 ReportToVehicleAdministratorFlow
就是为了向车管所 (vehicleAdministrator
) 共享该笔交易信息的。那么这个 Flow 的具体实现是什么呢。
@InitiatingFlow
class ReportToVehicleAdministratorFlow(
private val regulator: Party,
private val finalTx: SignedTransaction
) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(SendTransactionFlow(initiateFlow(regulator), finalTx))
}
}
稍微看下这个 flow 就会发现,Corda 几乎做了所有的事情,我们只用告诉 Corda 要向哪个节点 (regulator
) 共享哪笔交易的信息 (finalTx
) 就好了。在该 flow 中我们和 regulator
建立了一个 Session 连接,并调用了 SendTransactionFlow
发送交易信息。因此,我们还需要相应的响应者 (responder
) 来接收并处理 (ReceiveTransactionFlow
) 该交易。如下:
@InitiatedBy(ReportToVehicleAdministratorFlow::class)
class ReceiveVehicleAdministratorReportFlow(
private val otherSideSession: FlowSession
) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveTransactionFlow(
otherSideSession = otherSideSession,
checkSufficientSignatures = true,
statesToRecord = StatesToRecord.ALL_VISIBLE
))
}
}
ReceiveTransactionFlow
和 SendTransactionFlow
是相对应的,它接收并保存发送给它的交易信息。statesToRecord
属性确定该笔交易中哪些资产 (state
) 需要被记录在保险库 (Vault
) 中,在该例子中我们希望车管所记录该笔交易中所有的资产信息,所以我们将 statesToRecord
设置为 StatesToRecord.ALL_VISIBLE
。此外,根据不同的情景,我们也可以设置响应者存储不同的资产信息,只需将其设置为 StatesToRecord.ONLY_RELEVANT
甚至是 StatesToRecord.NONE
即可。而对于 checkSufficientSignatures
属性,它的目的则是检查分享的这一笔交易中是否所有的参与方都进行了签名,一般都是需要检查的,所有就设置为默认的 true
就好。
但其实,到此为止还只是实现了共享某一笔交易信息给某一个节点,要先实现广播,至少要先能够同时分享交易给多个节点吧,如下:
@InitiatingFlow
class BroadcastTransactionFlow(
private val recipients: List<Party>,
private val stx: SignedTransaction
) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
for (recipient in recipients) {
val session = initiateFlow(recipient)
subFlow(SendTransactionFlow(session, stx))
}
}
}
@InitiatedBy(BroadcastTransactionFlow::class)
class BroadcastTransactionResponder(private val session: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveTransactionFlow(session, statesToRecord = StatesToRecord.ALL_VISIBLE))
}
}
稍微看一下 BroadcastTransactionFlow
就会发现,我们其实并没有改多少代码,只是将一个 regulator
改为了 recipients: List<Party>
,这样就可以循环分享某一笔交易信息给指定的一些节点了。再进一步,能不能分享给全网所有的节点呢,其实也是可以的,如下:
subFlow(FinalityFlow(stx, sessions)).also {
// sends to everynode in the corda network
val broadcastToParties =
serviceHub.networkMapCache.allNodes.map {
node -> node.legalIdentities.first()
} - message.recipient - message.sender
subFlow(BroadcastTransactionFlow(broadcastToParties, it))
}
可以看到这里我们只是从 serviceHub
中找到所有的节点 allNodes
,减去这笔交易的发送者 (message.sender
) 和接受者 (message.recipient
) 不用发送之外,均广播给全网中其他的所有节点。
到了这里,我们已经完成了一个简易版的 Corda 广播实现。但在这里我们还需要额外注意一个问题,我们所广播到的每一个节点都是有权利使用我们分享给它的这笔交易中的资产的,所以我们在做与此类问题相关的设计时,即需要共享交易信息给非参与交易方时,需要额外考虑到这一点,是否需要加入必要的资产保护措施。尽管 Corda 是十分灵活的,但我们一定要对这种灵活性加以控制,以防出现我们所不期望的结果。
参考文档:
· https://docs.corda.net/tutorial-observer-nodes.html
· https://docs.corda.net/key-concepts-ledger.html
网友评论