有没有办法尝试通过 ID 获取合同并且在合同处于非活动状态时不会失败?

Is there a way to try to fetch a contract by ID and not fail if the contract is inactive?

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

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

这个问题有很多要解开的问题:

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

lookupByKey 和提议的 lookup 函数允许交易提交者对合约做出不存在的断言。来自 lookupByKeyNone 断言特定键不存在,来自 lookupNone 断言特定 ContractId 不存在。最大的区别在于合约的合约密钥包括密钥维护者,即验证查找的各方。因此很清楚谁必须授权 lookupByKey,以及谁验证否定结果。对于 lookupContractId 没有关联方,因此没有合理的授权或验证规则。

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

使用 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 必须是 "unchecked",这意味着提交者可以自由选择是走 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,这很烦人。可以想象 "unchecked queries" 在这里提供帮助。例如。可以引入一个函数 uncheckedContractActive : (Template t) => ContractId t -> Update Bool,它检查合约在提交者端是否处于活动状态,然后仅在交易中包含 Bool。很明显,提交者可以自由选择是否 运行 交易就像 cid 存在一样,或者相反。

到目前为止,未经检查的查询还没有进入 DAML,原因很简单,因为它们混淆了共享、保证和验证逻辑的画面,以及个人客户关心的是什么。在当前的设计中,该拆分恰好在 DAML 合约和分类帐客户端之间。

失败的交易实际上并没有那么昂贵,只要它们在解释期间在提交者节点上失败即可。重要的是设计合约模型和分类帐客户端时,fetch 调用只会因竞争条件而失败,并且合约上没有太多交易失败的争用。你可以试试

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

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