3

使用合约密钥有两个功能fetchByKeylookupByKey,后者允许我处理否定查询。我没有看到lookup : (Template t) => ContractId t -> Update (Optional t)对合同 ID 执行相同操作的函数。我也没有看到允许我处理失败fetch调用的 try-catch 机制。

如何在不重新实现整个 DAML 逻辑客户端的情况下避免失败的事务?

4

1 回答 1

5

这个问题有很多东西要解开:

  1. 为什么没有lookup
  2. 为什么没有 try-catch 块?
  3. 交易失败是否需要避免?
  4. 如何在不复制合约逻辑的情况下编写账本客户端?

lookupByKey提议的lookup功能允许交易的提交者对合同做出不存在的断言。None结果 fromlookupByKey断言特定键不存在,fromNone断言lookup特定键不ContractId存在。最大的区别在于合约的合约密钥包括密钥维护者,即验证查找的各方。因此,谁必须授权 alookupByKey以及谁验证否定结果是很清楚的。使用lookup,没有与 a 关联的各方,ContractId因此没有合理的授权或验证规则。

较弱的版本是fetchIfNotArchived : (Template t) => ContractId t -> Update (Optional t),如果 id 未知,则失败,None如果合同 id 已存档,则返回。DAML 交易对当前账本状态具有确定性,当前账本状态是所有活动合约的集合。诸如此类的功能fetchIfNotArchived将在 DAML 账本模型中引入不存在的合约和存档合约之间的区别,因此账本状态将从一开始就从所有活动合约变为整个账本。DAML 分类账不能再被截断并随着时间线性增长,这是不可取的。

使用 try-catch 块,我们会遇到非常相似的问题。假设我有某种尝试捕捉。我可以写这个函数:

lookup : (Template t) => ContractId t -> Update (Optional t)
lookup cid = do
  try do
    c <- fetch cid
    return (Some c)
  catch
    return None

如上所述,没有人可以合理地授权或验证提交者遵循了正确的路径。所以这样的 try-catch 必须是“未检查的”,这意味着提交者可以自由选择是走这条路try还是走这catch条路。您已经可以通过传递存在来模拟它:

maybeLookup : (Template t) => ContractId t -> Bool -> Update (Optional t)
maybeLookup cid cidIsKnown = if cidIsKnown
  then do
    c <- fetch cid
    return (Some c)
  else return None

这需要你cidIsKnown从外界传入,这很烦人。可以想象“未经检查的查询”在这里提供帮助。例如,可以引入一个函数uncheckedContractActive : (Template t) => ContractId t -> Update Bool,该函数检查合约在提交者端是否处于活动状态,然后仅将 包含Bool在交易中。很明显,提交者可以自由选择是否像 cid 存在一样运行事务,或者相反。

到目前为止,未经检查的查询还没有进入 DAML,原因很简单,它们混淆了共享、保证和验证逻辑的图景,以及单个客户的关注点。在当前的设计中,这种拆分恰好在 DAML 合约和分类账客户之间。

失败的交易实际上并没有那么昂贵,只要它们在解释期间在提交者节点上失败。重要的是以这样一种方式设计合约模型和账本客户端,使得fetch调用只会因为竞争条件而失败,并且合约上没有太多的争论,即交易失败很多。你可以试试

  • 设计每个自动化流程,使其跟踪“待定集”,即它期望通过其运行中的命令存档的一组合​​同 ID。这消除了每个自动化过程与自身的争用
  • 如果您有两个自动化的同一方始终竞争同一个合同,将合同一分为二,或将自动化合并为一个
  • 如果您为竞争同一合同的两方提供了两个自动化部分,请重新调整选择以使工作流程更加同步。即在 DAML 中控制轮到谁
  • 如果您的自动化部分偶尔会执行大批量操作,导致与其他自动化发生争用,请引入某种“守卫”。例如,将Lock合同写入分类帐,向非批处理自动化指示它应该暂停,或者使用Operation在批处理开始时被撤销的合同来保护选择。后者有更强的保证,但增加了开销,因为它fetch为每个选择增加了一个。

fetch一旦您将争用减少到可接受的水平,就可以使用分类帐客户端中的重试逻辑来处理失败的调用。

于 2019-09-24T07:41:10.740 回答