Corda IsRelevant() 变通了吗?

Corda IsRelevant() work around?

1.0 的 API 中有一些更改删除了 isRelevant()。最好的解决方法是什么?

给定一个用例:如果有 100 方想要查看此可查询状态和所有与之相关的更新(但只读且不需要签名),我是否需要将它们全部添加到参与者名单? "observer" 角色还不存在?对于仅查看静态参考数据的用例,是否有 alsoInformed 或类似的东西?

这里的一般功能是一个共享的线性可查询状态,其中发行者拥有change/update的主人,这将传播给所有想要"subscribe"这些变化的各方。我相信这可能适用于向 "club" 的广播,但我认为俱乐部尚不存在,或者它们是否是网络地图的动态分组。

在回答之前,我将介绍一些背景知识...平台中仍然存在相关性的概念。如您所知,在 Corda 中有两个数据持久存储;存储服务和保管库。

存储服务

存储服务是一个键 -> 持久化数据的值存储,例如:

  • 序列化流状态机
  • 附件
  • 交易

存储服务非常适合存储大量可以通过哈希索引和检索的序列化数据。然而,如果希望在其中一个存储的对象中搜索数据是很尴尬的。例如。使用交易存储时,无法轻松搜索特定类型的交易输出状态。该方法将遍历所有事务,将它们逐一反序列化,然后按输出类型进行过滤。这很麻烦而且效率不高。这就是保险库和相关性概念存在的原因!

金库

保险库的存在是为了存储状态对象,而不是交易。有一个 master states table,其中存储了状态引用、交易 ID(生成输出状态)和一些其他元数据,例如状态是否被消费(或未消费)。还有一个 LinearStates 的 table 和 OwnableStates 的 table。此外,如果有人希望将 ORM 添加到他们的状态,则会为反映 ORM 定义的每种类型的状态对象创建一个数据库 table。然后可以查询这些属性以从 vault 中提取满足特定查询的状态,例如"Any obligation states over £1000 with Alice as the lender that have not yet been consumed"。这就是金库的力量!

相关性

现在的情况是,并非节点接收到的所有交易都会产生与该节点相关的状态。一个例子是支付对支付交易,其中 Alice 将美元发送给 Bob,Bob 将英镑发送给 Alice。由于鲍勃现在拥有爱丽丝之前拥有的美元,这些美元现在与爱丽丝无关。因此,Alice 不应该记录代表这些美元的输出状态,因为她不持有对这些美元的权利和义务。 Alice 所做的是将旧的美元状态标记为已消耗,因此它现在不会计入她的总美元余额,也不能用作另一笔交易的输入(因为它已经被花费)。

Corda 中的相关性如何工作

因此,当节点收到新交易时,它会将每个输出状态的 participants 属性 中定义的 public 键与所有 public 键相交VaultService 知道。如果特定状态的结果集不为空,则该状态与节点相关。简单。

这意味着如果一个节点收到一个交易,其中他们的 public 键没有在输出状态的参与者字段中列出,那么他们将不会将该输出状态存储在保险库中。但是,他们会将交易存储在交易存储中,仍然可以查询。

OwnableState 的相关性概念很简单,要么拥有它,要么不拥有它。代表多边协议的 LinearState 的概念更为复杂。在 M14 及以下版本中,可以在 LinearState 中覆盖 isRelevant 的功能,但是在 V1 中,这已被删除,取而代之的是一种更简单的方法,该方法仅将参与者密钥与保管库密钥进行比较(如如上所述)。

V1 方法对相关性的影响

正如 OP 所指出的,在 V1 中,将有交易观察者的概念,其中不是状态参与者的节点仍然可以将状态存储在他们的保险库中并将其作为 "third party" 状态查询. IE。它不能被消耗或贡献于余额总数,但可以查询。与此同时,我们将不得不解决缺少该功能的问题,选项是:

  1. 对于 LinearStates,将所有预期的观察员添加到 participants 列表中。然后,向名为 consumers 的状态对象添加一个额外的 属性,它仅包含可以在有效交易中使用此状态的参与者列表,即签署包含它的交易。然后,该状态的合约会将命令中的 public 键与 consumers 列表中的键进行比较。这样,所有观察者仍会将状态存储在他们的保险库中。 FinalityFlow 会将此交易广播给所有 participants。如果您不想让其他参与者知道观察者,您可以使用随机生成的 public 密钥。
  2. 对于OwnableStates,和Cash一样,在participants中只能有一个身份,所有者。因此,方法是使用 FinalityFlow 将交易发送给一组观察者,然后这些观察者必须直接从交易中获取输出状态。麻烦但暂时的,因为我们目前正在处理交易观察者:https://r3-cev.atlassian.net/browse/CORDA-663.

如果这是在代码中,我所理解的只是一个稻草人。 即使用义务 cordapp 示例

义务状态

val consumers: List<AbstractParty> = listOf(lender, borrower)
override val participants: List<AbstractParty> get() = listOf(lender, borrower, extraActor)

合约代码验证

override fun verify(tx: LedgerTransaction){
        val command = tx.commands.requireSingleCommand<Commands>()
        when (command.value) {
            is Obligation.Issue -> requireThat {
                "The signers should only be the consumers for the obligation" using
                        (command.signers.toSet() == obligation.consumers.map { it.owningKey }.toSet())
            }

添加指定签名者在 TX 创建期间仅需是消费者的命令

val utx = TransactionBuilder(notary = notary)
                    .addOutputState(state, OBLIGATION_CONTRACT_ID)
                    .addCommand(Obligation.Issue(), state.consumers.map { it.owningKey })
                    .setTimeWindow(serviceHub.clock.instant(), 30.seconds)

这样,第一个tx允许extraActor在不签名的情况下将状态提交到账本中。在未来的 tx 提案中,这里的 extraActor 可以查询其状态 table 并使用不同的命令提出状态生命周期的变化,这一次它可能需要所有参与者(如果需要)签署命令。即 Obligation.DoSomethingExtra 所有参与者签名的命令 (command.signers.toSet() == obligation.participants.map { it.owningKey }.toSet())