账本模型(详解)
Canton 账本模型参考:合约生命周期、交易结构与隐私规则。
Canton 账本模型的正式规范:结构、有效性、完整性、隐私性和多同步器互操作性。
本节介绍分类账的结构,记录分类账变化时各方之间的交互。这里提出的定义解决了第一个问题:“变更和账本是什么样的?”。记录的交互的基本构建块是操作,它们被分组为事务、更新、提交和账本。
运行工作流程示例
本节中的大多数示例都会基于运行示例中的模板来查看以下 Daml 脚本场景。两家银行首先分别向 Alice 或 Bob 发行一项资产,然后 Alice 向 Bob 提出 DvP。 Bob 接受提案并解决 DvP。
let eurAsset = SimpleAsset with
issuer = bank1
owner = alice
asset = "1 EUR"
eur <- submit bank1 do createCmd eurAsset
let usdAsset = SimpleAsset with
issuer = bank2
owner = bob
asset = "1 USD"
usd <- submit bank2 do createCmd usdAsset
proposeDvP <- submit alice $ do
createCmd ProposeSimpleDvP with
proposer = alice
counterparty = bob
allocated = eur
expected = usdAsset
disclosedEur <- fromSome <$> queryDisclosure alice eur
接受和结算可以通过AcceptAndSettle选择一步完成。
(newUsd, newEur) <- submitWithDisclosures bob [disclosedEur] do
exerciseCmd proposeDvP $ AcceptAndSettle with toBeAllocated = usd
或者分两个单独的步骤,先使用Accept,然后使用Settle:
dvp <- submit bob $
do exerciseCmd proposeDvp $ Accept with toBeAllocated = usd
(newUsd, newEur) <- submitWithDisclosures bob [disclosedEur] do
exerciseCmd dvp $ Settle with actor = bob
行动
层次结构
账本模型的主要特征之一是分层操作结构。此结构通过 Bob 在上述场景中通过行使 Settle 选择来解决 DvP 来说明。 Alice 和 Bob 已将账本上的资产(合约 #1 和 #2)分配给 SimpleDvp 合约 (#4)。这些合约在下图中显示为输入(左侧虚线框)。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-settle-action.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=92dcd6bdccf7c5050568bbe072b6cb47” alt=“Alice 和 Bob 之间对 SimpleDvp 合约的结算行动,以互换的两条腿作为结果。“宽度=“1642”高度=“1093”数据路径=“images/docs_website/dvp-settle-action.svg”/>
行使 Settle 选择会产生“行使”操作,即以蓝色显示的节点树。左侧的输入合约不是操作的一部分。根节点描述了选择的参数并引用SimpleDvp输入合约#4。它有两个子树,作为 Settle 选择的一部分自动执行资产转移。
- 左子树代表 Alice 对她的
SimpleAsset合约#1 行使Transfer选择。它由两个节点组成:根节点描述选择的参数和输入合约#1。子节点是它自己的单节点子树,对 Bob 的新SimpleAsset合约 #5 的创建进行编码。 - 右子树类似:子树的根节点描述了 Bob 对他的
SimpleAsset合约 #2 行使Transfer选择,其子节点编码了 Alice 新的SimpleAsset合约 #6 的创建。
值得注意的是,即使根节点已经描述了所有相关参数,练习操作也是整个树。账本模型关注的是操作而不是节点,因为根节点不能在没有子节点的情况下单独存在,因为 Daml 模型中的选择主体在执行选择时必须始终执行。完整性部分对此进行了详细介绍。然而,动作并不是不可分割的,而是分层的:左子树和右子树本身就是动作,即 Alice 和 Bob 分别在其 SimpleAsset 输入合约 #1 和 #2 上行使 Transfer 选择的行使动作。两个子树中的每一个都包含另一个子树,即 Bob 和 Alice 的新SimpleAsset合约#5和#6的创建。每个子树本身都是一个动作。这种层次结构产生了下面解释的子动作关系,并形成了隐私模型的基础。
定义
总的来说,上例中的结算包含两类动作:
- 创建合约
- 行使合同选择权。
这也是账本模型中的两种主要行为。
节点 是以下之一:
1、Create节点记录了合约的创建。它包含以下信息:
- 合约ID是合约的唯一标识符。它相当于账本中基于未花费交易输出(UTxO)的交易输出(TxO)。
- 模板 ID 标识与合约关联的 Daml 代码,其参数定义 合约实例,它是与合约 ID 关联的不可变数据。
- 签字人 是必须授权合同创建和存档的非空各方。
- 合同观察员,或简称观察员,是除签署方之外还将了解合同创建和归档情况的一组各方。
在Daml中,签署者和合约观察者由模板定义的signatory和observer条款确定。
创建节点如下图所示。图表通常会省略具有空值的字段和同时也是签名者的观察者。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/create-node.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=9c64445fa65cb58481369dade79bc801” className=“align-center” style={{width: “30.0%”}} alt=“创建节点的结构。“宽度=“442”高度=“202”数据路径=“images/docs_website/create-node.svg”/>
-
Exercise 节点记录一方或多方对合约执行的选择的参数。它包含以下信息:
- 一种类型,可以是消耗性,也可以是非消耗性。合约一旦消耗,就不能再次使用;例如,爱丽丝不能两次转移她的资产,因为这将是双重支出。相比之下,以非消费方式行使的合同可以重复使用,例如用于表达从一方到另一方的委托。
- 执行选择的 合约 ID。该合约称为输入合约。
- 接口 ID(如果通过 Daml 接口执行此选择)。
- 模板 ID,定义具有给定 选择名称 的选择的智能合约代码;以及传递给智能合约代码的选择参数。
- 一组称为 参与者 的相关方。这些是执行该操作的各方。它们在 Daml 模板的
controller子句中指定。 - 一组相关的选择观察员。这些各方将被告知所做出的选择。
- 练习结果作为通过评估选择主体返回的 Daml 值。
练习节点如下所示,其中结果由从左到右排列的箭头指示。如果它是消耗性的、空字段值以及同时也是参与者的选择观察者,则图表会省略该类型。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/exercise-node.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=52697fa7f7bd95a232ddd2c1588c62b6” className=“align-center” style={{width: “30.0%”}} alt=“Exercise 节点的结构。“宽度=“442”高度=“282”数据路径=“images/docs_website/exercise-node.svg”/>
-
合约上的 Fetch 节点,表明合约存在并且在获取时处于活动状态。获取的行为类似于非消耗性练习,没有任何后果,并且可以重复。获取节点包含以下信息,类似于练习节点:合约 ID、接口 ID、模板 ID 和 参与者,即获取合约的各方。获取节点如下图所示。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/fetch-node.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=6c854090ce405fc45ba634de15d55ca1” className=“align-center” style={{width: “30.0%”}} alt=“Fetch 节点的结构。“宽度=“442”高度=“202”数据路径=“images/docs_website/fetch-node.svg”/>
操作由一个根节点和一系列结果组成,这些结果本身就是操作。这就产生了动作的树结构:动作的根节点将其后果的根节点作为子节点。
动作从其根节点继承其类型:
- 创建操作有一个创建节点作为根。后果都是空的。
- 锻炼操作以锻炼节点作为根,结果是子操作。锻炼动作是其后果的父动作。
- Fetch 操作 作为 Fetch 节点作为根。后果都是空的。
节点上的术语扩展到通过根节点的操作。例如,“创建”操作的签名者是“创建”节点的签名者,而“练习”操作当且仅当其根节点是(非)消耗的。此外,对合约的执行或获取操作被称为“使用”合约。最后,消耗练习被称为消耗(或存档)其合约。
示例
获取操作的示例出现在模板 ProposeSimpleDvP 的 DvP 提案合同的 Accept 选项中。选择主体获取 Bob 分配给 DvP 的SimpleAsset,DvP 检查资产合约是否处于活动状态并将合约实例带入计算中,以便选择实现可以断言该资产满足提案合约中表达的期望。下图显示了此“练习”操作,其中“获取”操作是其第一个结果。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-propose-accept-action.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=7985f68cb49127abf9d49ef7688d6c67” className=“align-center” style={{width: “100.0%”}} alt=“Bob 对 Alice 的 ProposeSimpleDvP 执行的接受操作。“宽度=“1562”高度=“715”数据路径=“images/docs_website/dvp-propose-accept-action.svg”/>
非消耗性行使出现在ProposeSimpleDvP合约上的组合AcceptAndSettle选择中:该选择是非消耗性的,因此在选择主体中行使的Accept选择可以消耗提案合约。如下图所示,非消耗练习会产生对同一输入合约 #3 的多个引用。该图还显示提取具有相同的效果:输入合约 #2 使用两次。
<图片src =“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-propose-accept-a nd-settle-action.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=7fb2fa37d4a5585387ad222a9afe9349” className=“align-center” style={{width: “100.0%”}} alt=“Bob 对 Alice 的 ProposeSimpleDvP 执行的接受并结算操作。“宽度=“2283”高度=“1255”数据路径=“images/docs_website/dvp-propose-accept-and-settle-action.svg”/>
子操作
这个例子再次强调了动作的层次结构:AcceptAndSettle动作包含Accept和Settle的相应动作作为其结果。
更一般地,对于动作act,其真子动作是act结果中的所有动作,以及它们的所有真子动作。此外,act本身就是一个(不正确的)子动作。
Bob 的 Settle 练习的子动作关系如下图所示。每个无边界框都包含一个操作(通过其节点树),并且这些框的嵌套对子操作关系进行编码。详细来说,蓝色和紫色框都是 Bob 的灰色显示的 Settle 动作的真子动作。绿色框是蓝色框和灰色框的真子函数,黄色框是紫色框和灰色框的真子函数。
<图片src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-settle-subactions.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=18826aee2e0d531ab1f060dfc332fc00” className=“align-center” style={{width: “60.0%”}} alt=“Bob 在 DvP 合约上行使 “Settle` 选择的子操作。“宽度=“1122”高度=“1122”数据路径=“images/docs_website/dvp-settle-subactions.svg”/>## 交易
事务是原子执行的操作列表。这些操作称为事务的根操作。也就是说,对于事务tx = act``1``, …, act``n,每个act``i都是一个根操作。例如,如果 Alice 和 Charlie 分别为 Bob 提出了一项 DvP 提案,则 Bob 可能希望同时接受。为此,Bob 在单个事务中通过两个根操作(蓝色和紫色)执行两个 Accept 选择,如下所示。从视觉上看,事务由两侧的虚线分隔,以将其与操作区分开。与操作一样,左侧的输入合约不是交易的一部分。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-accept-two.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=ee3f6ea1f8df103166460369377101d2” className=“align-center” style={{width: “100.0%”}} alt=“具有两个顶级操作的交易,其中 Bob 接受两个 DvP 提案,一个来自 Alice,一个来自 Charlie。“宽度=“2442”高度=“1155”数据路径=“images/docs_website/dvp-accept-two.svg”/>
再举个例子,练习操作的结果是一个操作列表,因此形成一个交易。在 Alice 和 Bob 的 SimpleDvP 上的 Settle 操作示例中,Settle 操作的结果形成以下交易,其中操作像以前一样从左到右排序。该交易由两个根动作(蓝色和紫色)组成,即DvP两条腿的两个Transfer动作。
<图片src =“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-settle-consequence s-are-transactions.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=5b0538ddeb688764316720634ea98d6a” className=“align-center” style={{width: “50.0%”}} alt=“DvPSettle action are a transaction of two actions, namely the two Transfer腿的后果。“宽度=“962”高度=“612”数据路径=“images/docs_website/dvp-settle-consequences-are-transactions.svg”/>
操作的层次结构扩展到事务并产生子事务的概念。交易的适当的子交易是通过(反复)用结果替换操作来获得的;交易的子交易要么是交易本身,要么是其适当的子交易。
例如,鉴于上面显示的事务仅包含 Settle 操作的两个结果,下图显示了所有七个正确的非空子事务,每个子事务都带有虚线分隔符。
<图片src =“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-settle-consequence s-subtransactions.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=809e7066dbf0b44649bada35081b06d9” className=“align-center” style={{width: “100.0%”}} alt=“Settle 操作结果的所有正确子事务。“宽度=“1942”高度=“1308”数据路径=“images/docs_website/dvp-settle-consequences-subtransactions.svg”/>
隐私模型使用子交易的概念来定义可见性规则。
输入和输出
账本模型属于(扩展的)UTxO 式账本类别,其中未花费的交易输出(UTxO)集构成了账本的当前状态。这里,交易输出是在交易中创建的合约的合约ID。当合约被消耗时,其合约 ID 也会被消耗,从而从 UTxO 集中删除。与每个UTxO相关的数据是不可变的;通过使用合约 ID 并使用不同的合约 ID 重新创建新合约来进行修改。
该账本模型在两个方面扩展了 UTxO 模型:
- 交易可以使用合约而不消耗它,例如通过执行非消耗选择或获取它。在这种情况下,合约 ID 仍保留在 UTxO 集中,即使它显示为交易的输入。
- 交易是分层结构的,交易中创建的合约 ID 可以在同一交易中使用。例如,在
AcceptAndSettle操作中,第一个结果中创建的SimpleDvP被第二个结果消耗。此类合约称为临时。
这些方面将在账本模型的其余部分中进行更详细的讨论。
账本
交易结构记录了一方交互的内容。账本记录了交互的另外两个方面:* 唯一引用特定方交互的标识符。
- 请求特定参与方互动的参与方。
由于隐私模型的原因,并不是每个人都能看到聚会互动的所有部分。参与方交互的唯一标识符允许不同参与方关联他们是否看到相同交互的部分。 更新的概念添加了这样一个标识符。它由单个事务和所谓的更新 ID(一个字符串)组成。账本模型中的示例使用 TX i 形式的更新 ID 来表示某些数字 i,类似于 Daml Studio 中的交易视图。在 Ledger API 上,更新 ID 是任意字符串,其字典顺序与其在分类帐上的顺序无关。
提交添加谁请求参与方交互的信息。它由更新和请求更新的一方或多方组成。这些各方称为提交的请求者。在Daml脚本中,请求者对应于给予submit命令的actAs方。
Ledger 是提交的有向无环图 (DAG),其中更新 ID 是唯一的。
对于提交,其事务的根操作称为顶级操作。任何账本提交的顶级操作也是账本的顶级操作。
因此,《Canton总账》代表了各方所采取的所有行动的完整历史。账本的图形结构在账本中的提交上产生了发生之前的顺序。我们说提交c``1 发生在 c``2当且仅当分类帐包含从c``1到c``2的非空路径,或者等效地,图的传递闭包包含来自c``1至c``2。
从视觉上看,账本可以表示为随着时间的推移从左到右增长的序列。下面,紫色垂直虚线标记了提交的边界,每个提交都标有其请求者和更新 ID。蓝色箭头将每个“Exercise”和“Fetch”操作链接到输入合约的“Create”操作。这些箭头强调账本形成了 UTXO 区块链意义上的交易图。
例如,以下 Daml 脚本对运行的 DvP 示例的整个工作流程进行编码。
let eurAsset = SimpleAsset with
issuer = bank1
owner = alice
asset = "1 EUR"
eur <- submit bank1 do createCmd eurAsset
let usdAsset = SimpleAsset with
issuer = bank2
owner = bob
asset = "1 USD"
usd <- submit bank2 do createCmd usdAsset
proposeDvP <- submit alice $ do
createCmd ProposeSimpleDvP with
proposer = alice
counterparty = bob
allocated = eur
expected = usdAsset
disclosedEur <- fromSome <$> queryDisclosure alice eur
(newUsd, newEur) <- submitWithDisclosures bob [disclosedEur] do
exerciseCmd proposeDvP $ AcceptAndSettle with toBeAllocated = usd
此工作流程产生如下所示的分类账,其中有四次提交:
- 在第一次提交中,银行 1 请求创建发放给 Alice 的
SimpleAsset或1 EUR(合约 #1)。 - 在第二次提交中,Bank 2 请求创建发放给 Bob(合约 #2)的
1 USD的SimpleAsset。 - 在第三次提交中,Alice 请求创建
SimpleDvpPoposal(合约 #3)。 - 在第四次提交中,Bob 请求对 DvP 提案行使
AcceptAndSettle选择权。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-ledger.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=2905a7d8b725484c0035079b45d2ba07” className=“align-center” alt=“整个 DvP 工作流程的提交顺序。首先,银行 1 和 2 发行资产,然后 Alice 提出 DvP,最后 Bob 接受并结算。“宽度=“3322”高度=“1362”数据路径=“images/docs_website/dvp-ledger.svg”/>TX 0、TX 1和TX 2之间不需要有边,因此它们可以按任何顺序呈现。
由于账本是一个 DAG,因此我们总是可以通过拓扑排序将顺序扩展为线性序列。对于接下来的部分,我们假设账本是完全有序的(除非另有说明)。我们在因果关系部分讨论更一般的偏序。
{/* COPIED_START source=“docs-website:docs/replicated/daml/3.4/overview/explanations/ledger-model/ledger-validity.rst” hash=“3710723f” */}
有效账本
其核心是“有效账本”的概念;如果将相应的提交添加到分类帐中会产生有效的分类帐,则允许进行更改。 有效的账本是满足三个条件的账本:
da-model-consistency
不允许对非活动合约进行练习和提取,即尚未创建或已被练习消耗的合约。仅当密钥未与另一个未使用的合约关联且所有密钥断言均成立时,才能创建具有合约密钥的合约。
da-model-conformance
给定合约只允许执行一组受限的操作。
da-model-authorization
可能要求特定变更的各方受到限制。
这些条件中只有最后一个取决于请求变更的一方(或多方);另外两个就一般了。
{/* COPIED_START source=“docs-website:docs/replicated/daml/3.4/overview/explanations/ledger-model/ledger-integrity.rst” hash=“c93dba12” */}
诚信
本节解决谁可以请求哪些更改的问题。
为了回答下一个问题“谁可以请求哪些更改”,需要精确定义哪些分类账是允许的,哪些是不允许的。例如,上面的油漆报价账本直观上是允许的,而以下所有账本都不是。
<图>
<图>
<图>
<图>
<图>
一致性
一致性由两部分组成:
1.合约一致性:合约必须在使用之前创建,一旦消费就无法使用。 2. 密钥一致性:密钥是唯一的并且满足密钥断言。
为了准确定义这一点,需要“之前”和“之后”的概念。这些是通过将所有操作按顺序排列而给出的。从技术上讲,序列是通过对账本操作进行预序遍历来获得的,注意这些操作形成了一个(有序的)森林。直观上,它是通过始终在其适当的子操作之前选择父操作来获得的,否则总是在右侧的操作之前选择左侧的操作。下图描述了油漆报价示例中的最终订单:
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/consistency-order-on-actions.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=dad56899a6c7f7695b4b59005554d5cf” className=“align-center” style={{width: “100.0%”}} alt=“提交的时间顺序。第一次提交,银行请求 Iou Bank A。第二次提交,P 请求 PaintOffer P A P123。最后一次提交,A 请求,Exe A (PaintOffer P A P123) 导致 Exe A (Iou Bank A) 导致 Iou Bank P 导致 PaintAgree P A P123” width=“1251” height=“518” data-path=“images/docs_website/consistency-order-on-actions.svg” />
在图像中,如果存在从 act 到 act' 的(非空)路径,则操作 act 在操作 act' 之前发生。然后,act'发生在act之后。
合约一致性
合约一致性确保合约在创建之后和使用之前被使用。
- 要么
act本身 Create c 要么 Create c 发生在act之前 act不会在任何 Create c 操作之前发生- 任何消耗
c的锻炼动作后都不会发生act。
一致性条件排除了双花示例。正如下面的红色路径所示,示例中的第二次练习发生在对同一合约进行消耗练习之后,违反了合约一致性标准。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/consistency-banning-double-spends.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=f905e3b64ca7636365f4631da7185fe1” className=“align-center” style={{width: “100.0%”}} alt=“另一个提交时间序列。在第一次提交中,Iou Bank A 被银行请求。在第二次提交中,Exe A (Iou Bank A) 通过红线通向 Iou Bank B,表明违反了合约一致性。在第三次提交中,Iou Bank B 通过红线通向 Exe A (Iou Bank A),Exe A (Iou Bank A) 通向 Iou P 银行。”宽度=“1220”高度=“362”数据路径=“images/docs_website/consistency-banning-double-spends.svg”/>
- 主动,如果
c最新的状态变化是创建; - 已存档,如果
c的最新状态更改是一项消耗性的练习; - 不存在,如果
c从未改变过状态。
如果仅当 c 处于活动状态时才发生c 上的 Exercise 和 Fetch 操作,并且仅当 c 不存在时才发生 Create 操作,则c 的账本是一致的。下图直观地展示了示例账本中所有点的不同合约的状态。<图>
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/consistency-paint-offer-activeness.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=166f2ac81a3fbf9309dd910c050956cf” className=“align-center” style={{width: “100.0%”}} alt=”./images/consistency-paint-offer-activeness.svg” width=“1251” height=“502” data-path=“images/docs_website/consistency-paint-offer-activeness.svg” />
<图> <img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/consistency-alice-iou-activeness.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=1707ba950349e96fa049806cf0dc1350” className=“align-center” style={{width: “100.0%”}} alt=”./images/consistency-alice-iou-activeness.svg” width=“1251” height=“502” data-path=“images/docs_website/consistency-alice-iou-activeness.svg” />
顺序的概念可以在所有不同的账本结构上定义:操作、交易、交易列表和账本。因此,一致性、输入和输出以及合约状态的概念也可以在所有这些结构上定义。账本的活跃合约集是账本上所有活跃合约的集合。对于上面的例子,它由合约Iou Bank P和PaintAgree P A组成。
关键一致性
合约密钥为账本引入了关键的唯一性约束。为了捕捉这个概念,合约模型必须为系统中的每个合约指定该合约是否有密钥,如果有,则指定该密钥。每个合约最多可以有一个密钥。
就像合约一样,每个密钥都有一个状态。动作 act 是一个 按键上的动作 k 如果
act是对带有密钥k的合约c的 创建、执行或 获取 操作,或者act是关键断言 NoSuchKeyk。
- 如果
act是对合约c的创建、非消耗练习或获取操作,则密钥状态被分配给c。 - 如果
act是消耗 Exercise 操作或 NoSuchKey 断言,则密钥状态为 free。 - 如果没有这样的动作
act,则按键状态为未知。
如果密钥状态为 空闲 或 未知,则该密钥未分配。
密钥一致性确保每个密钥最多有一个有效合约,并且所有密钥断言都得到满足。
- 如果
act是 Create 操作或 NoSuchKey 断言,则s是 自由 或 未知。 - 如果
act是某个合约c上的 Exercise 或 Fetch 操作,则s分配 到c或 未知。
密钥一致性排除了有关密钥一致性的有问题的示例。例如,假设油漆工P已向A提出了参考编号为P123的油漆报价,但A尚未接受。当P尝试使用相同的参考号P123为David创建另一个油漆报价时,此创建操作将违反密钥唯一性。以下分类帐违反了密钥(P, P123)的密钥唯一性。
<图>
</图>关键断言可以在工作流程中使用来证明某种合同不存在。例如,假设画家
P是画家工会U的成员。该联盟保留了一份潜在客户黑名单,其成员不得与之开展业务。如果存在有效合同Blacklist @U &A,则客户A 被视为在黑名单上。为了确保在 A 被列入黑名单的情况下,画家 P 不会提供绘画报价,画家将其提交与密钥 (U, A) 上的 NoSuchKey 断言结合起来。以下账本显示了该交易,其中UnionMember U P代表P在联盟U中的成员资格。它授予P执行此类断言的选择,这是授权所需的。
<图>
</图>
就像其他一致性概念一样,密钥一致性扩展到操作、事务和事务列表。
账本一致性
定义»账本一致性« 如果所有合约和所有密钥都一致,则分类账是一致的。
内部一致性
对于孤立的操作和事务来说,上述一致性要求太强了。例如,油漆报价示例中的承兑交易与分类账不一致,因为PaintOffer A P Bank和Iou Bank A合约在之前没有创建的情况下使用:
然而,交易仍然可以附加到创建这些合约并产生一致的分类账的分类账中。此类交易被称为内部一致,诸如PaintOffer A P Bank P123和Iou Bank A之类的合约称为交易的输入合约。对偶而言,交易的输出合约是交易创建但不存档的合约。
act不会在任何 Create c 操作之前发生- 消耗
c运动后不会发生act。
如果一笔交易对于所有合约都内部一致并且对于所有密钥都一致,那么该交易就是内部一致。
请注意,对于内部不一致的交易,输入和输出合约是未定义的。下图显示了内部一致和不一致事务的一些示例。
<图> <img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/internal-consistency-examples.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=ac4fdf1301dcb0ae79c2da90326df85f” className=“align-center” style={{width: “100.0%”}} alt=”./images/internal-consistency-examples.svg” width=“1100” height=“583” data-path=“images/docs_website/internal-consistency-examples.svg” />
与输入合约类似,我们将输入键定义为在交易开始时必须取消分配的集合。
定义»输入键«
如果 k 上的第一个操作 act 是 Create 操作或 NoSuchKey 断言,则密钥 k 是内部一致事务的 输入密钥。在黑名单示例中,P的交易有两个输入密钥:(U, A)(由于NoSuchKey操作)和(P, P123)(因为它创建了PaintOffer合约)。
一致性
一致性条件限制了账本上可能发生的操作。这是通过考虑 合约模型 M(或简称 模型)来完成的,它指定了所有可能操作的集合。如果账本上的所有顶级操作都是M的成员,则账本符合M(或符合M)。与一致性一样,一致性的概念不依赖于提交的请求者,因此它也可以应用于事务和事务列表。
例如,IOU 合约上允许的操作集可以描述如下。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/models-simple-iou.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=367e3af7930fd69042909a29a8eff573” className=“align-center” style={{width: “80.0%”}} alt=“IOU 合约上允许的一组创建、转移和结算操作,如下面的段落所述。“宽度=“907”高度=“420”数据路径=“images/docs_website/models-simple-iou.svg”/>
图像中的框是模板,因为框内的合约参数(例如义务人或所有者)可以通过适当类型的任意值来实例化。为了便于理解,每个框都包含一个标签,描述相应操作集的直观目的。如图所示,转账箱施加了约束,即银行必须在已执行的 IOU 合约和新创建的 IOU 合约中保持不变。但是,所有者可以任意更改。相反,在和解行动中,银行和所有者必须保持不变。此外,为了保持一致,转让行为的参与者必须与合同的所有者相同。
当然,对参数之间关系的约束可以是任意复杂的,并且不能方便地在该图形表示中再现。这就是 Daml 的作用——它提供了一种更方便的方式来表示合约模型。 Daml 和合约模型之间的联系将在后面的部分中更详细地解释。
要查看实际的一致性标准,假设合约模型仅允许对 PaintOffer 和 PaintAgree 合约执行以下操作。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/models-paint-offer.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=18c41c80ee34b639d2667e24cd9cb211” className=“align-center” style={{width: “90.0%”}} alt=“PaintOffer 和 PaintAgree 合约上可用的创建和接受操作。“宽度=“942”高度=“551”数据路径=“images/docs_website/models-paint-offer.svg”/>
爱丽丝更改要约结果以避免转移资金的示例的问题现在变得显而易见。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/non-conformant-action.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=8c316201ce7b19be6d74a60c83d5e1fc” className=“align-center” alt=“说明问题的时间顺序,如下所述。“宽度=“1220”高度=“502”数据路径=“images/docs_website/non-conformant-action.svg”/>
A 的提交不符合合约模型,因为该模型不包含她尝试提交的顶级操作。
授权
最后一个标准排除了最后两个有问题的例子,即强加给画家的义务以及画家偷了爱丽丝的钱。其中第一个如下所示。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/invalid-obligation.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=87da739c05b2ea006c1370a44095d7c4” className=“align-center” style={{width: “100.0%”}} alt=“仅显示一次提交的时间序列,其中 A 请求 PaintAgree P A P123。“宽度=“1220”高度=“242”数据路径=“images/docs_website/invalid-obligation.svg”/>
这个例子直观上不允许的原因是,PaintAgree合同应该表达油漆工有义务粉刷爱丽丝的房子,但他从未同意这项义务。在纸质合同中,义务在合同正文中表达,并强加给合同的签署人。
签署者和维护者为了捕获现实世界合约的这些元素,合约模型还为系统中的每个合约指定:
- 非空的签字人集合,即受合同约束的各方。
- 如果合约与密钥相关联,则为一组非空的 维护者,确保该密钥最多存在一个未使用的合约的各方。维护者必须是签名者的子集并且仅依赖于密钥。这种依赖性由函数
maintainers捕获,该函数接受密钥并返回密钥的维护者。
在示例中,合约模型指定
Iou obligor owner合约只有obligor作为签署人。MustPay obligor owner合约有obligor和owner作为签署人。PaintOffer houseOwner painter obligor refNo合同只有油漆工作为签字人。其关联键由画家和参考号组成。画家是维护者。PaintAgree houseOwner painter refNo合同有房主和油漆工双方签字。密钥由油漆工和参考号组成。画家是唯一的维护者。
在下面的图形表示中,合同的签署人用美元符号(作为义务的助记符)表示,并使用粗体。维护者被标记为@(作为强制唯一性的助记符)。由于维护者始终是签名者,因此标有 @ 的各方是隐式签名者。例如,注释油漆要约接受行动与签名者会产生下图。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/signatories-paint-offer.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=f59d4caeae372b52ca5a79b4043248b9” className=“align-center” style={{width: “60.0%”}} alt=“原始油漆交易流程图。P 是维护者;A 和银行是签署者。“宽度=“620”高度=“363”数据路径=“images/docs_website/signatories-paint-offer.svg”/>
授权规则
签名者允许人们准确地声明画家有一项义务。强加的义务直观上是无效的,因为画家不同意这项义务。换句话说,画家没有“授权”创造义务。
在 Daml 账本中,一方可以通过以下方式之一授权提交的子操作:
- 提交的每个顶级操作均由提交的所有请求者授权。
- 合同
c上的行使行动act的每项后果均由c的所有签署者和act的所有参与者授权。
第二条授权规则编码了要约-接受模式,这是合同法中合同成立的先决条件。合同c实际上是作为要约人的签署者的要约。该活动是受要约者接受要约。执行的结果可以解释为合同主体,因此 Daml 账本的授权规则与合同法中合同形成的规则密切相关。
- 合约
c的创建操作所需的授权者是c的签署人。 - Exercise 或 Fetch 操作所需的授权者是其参与者。
- NoSuchKey 断言所需的授权者是密钥的维护者。
我们将这个概念提升到分类账,即当分类账的所有提交都得到充分授权时。
示例
通过查看一些示例,可以很容易地直观地了解授权定义的工作原理。主要的例子,油漆报价分类账,直观上是合法的。因此,根据我们的定义,它也应该得到充分授权,事实确实如此。
在下面的可视化中,Π ✓ act 表示各方Π 授权操作act。授权结果如下所示。<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/authorization-paint-offer.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=489df8e52d59814ff917a1f669b1e202” className=“align-center” alt=“原始油漆处理时间顺序,根据下面的授权进行深入描述。“宽度=“1311”高度=“502”数据路径=“images/docs_website/authorization-paint-offer.svg”/>
在第一次提交中,银行通过请求该提交来授权创建 IOU。由于银行是 IOU 合同的唯一签署人,因此该承诺具有充分的授权。同样,在第二次提交中,画家授权创建油漆报价合同,并且画家是该合同的唯一签署人,使得该提交也得到了充分授权。
第三次提交更加复杂。首先,Alice 通过请求来授权对油漆报价的行使。她是这次演习中唯一的演员,因此这符合授权要求。由于画家是油漆报价的签字人,而爱丽丝是演习的参与者,因此他们共同授权演习的所有后果。第一个结果是对 IOU 的练习,以 Alice 为主角,所以这是允许的。第二个结果是通过执行旧的 IOU(对于 A)创建新的 IOU(对于 P)。由于借条之前是由银行签署的,Alice是演练的主角,所以他们共同授权了这次创作。由于银行是唯一签署人,因此该行为是允许的。最终的结果是与爱丽丝和画家作为签字人签订了绘画协议。既然他们都授权了这一行动,那么这也是允许的。因此,整个第三次提交也是经过良好授权的,账本也是如此。
同样,我们的授权标准禁止直观上有问题的示例。在第一个例子中,爱丽丝强迫油漆工粉刷她的房子。该示例的授权如下所示。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/authorization-invalid-obligation.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=d200a3bae13cf1b9de6cbf4664112c70” className=“align-center” alt=“爱丽丝强迫油漆工粉刷她的房子的场景的时间序列,下面将根据授权进行深入描述。“宽度=“1220”高度=“242”数据路径=“images/docs_website/authorization-invalid-obligation.svg”/>
Alice 通过请求授权对 PaintAgree 合约执行 Create 操作。不过,画师也是PaintAgree合约的签署人,但他并没有授权创建动作。因此,这个账本确实没有得到很好的授权。
在第二个例子中,画家从爱丽丝那里偷了钱。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/authorization-stealing-ious.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=46bd84bcbf8f39ac6fff1787bc759dc4” className=“align-center” alt=“画家偷了爱丽丝的钱的场景的时间序列,下面将根据授权进行深入描述。“宽度=“1220”高度=“355”数据路径=“images/docs_website/authorization-stealing-ious.svg”/>
银行通过请求此操作来授权创建借据。同样,画家授权将欠条转让给他。然而,本次演习的执行者是 Alice,她并未授权此次演习。因此,这个账本没有得到很好的授权。
有效的账本、义务、要约和权利
Daml 账本旨在模仿现实世界中各方之间的互动,并受合同法管辖。账本上的有效性条件以及合同模型中包含的信息与合同法的概念有一些微妙的联系,值得指出。
首先,合同规定了隐含的账本义务,这是由于合同执行的后果而产生的。例如,PaintOffer 包含A 的账本义务,如果她接受要约,则转移她的 IOU。
其次,Daml 账本上的每个合约都可以模拟真实世界的报价,其结果(账本内和账外)由合约模型允许的合约上的 Exercise 操作指定。第三,在Daml账本中,就像在现实世界中一样,一个人的权利就是另一个人的义务。例如,A接受PaintOffer的权利,而P则有义务在她接受的情况下粉刷她的房子。在Daml账本中,合同模式下的一方权利是基于授权和一致性规则,该方可以执行的行使行为。
最后,有效性条件确保了 Daml 账本模型的三个重要属性,模仿合同法。
-
义务需要同意。 Daml 账本遵循合同法的要约-接受模式,从而确保所有账本合同都是自愿形成的。例如,以下分类帐无效。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/authorization-invalid-obligation.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=d200a3bae13cf1b9de6cbf4664112c70” className=“align-center” style={{width: “100.0%”}} alt=“Alice 强迫油漆工粉刷她的房子的场景的时间顺序,如前面的授权规则示例部分所述。“宽度=“1220”高度=“242”数据路径=“images/docs_website/authorization-invalid-obligation.svg”/>
-
需要同意才能取消账本权利。由于只有行使行动消耗合约,因此不能剥夺行动者的权利;合约模型明确指定了参与者是谁,授权规则要求他们批准合约消费。
在例子中,Alice 有权转让她的 IOU;画家试图通过自己进行转移来立即从她手中夺走这一点,但这是无效的。
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/authorization-stealing-ious.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=46bd84bcbf8f39ac6fff1787bc759dc4” className=“align-center” style={{width: “100.0%”}} alt=“画家偷走爱丽丝的钱的场景的时间顺序,之前在授权规则示例部分中进行了解释。“宽度=“1220”高度=“355”数据路径=“images/docs_website/authorization-stealing-ious.svg”/>
各方仍然可以将其权利“委托”给其他方。例如,假设爱丽丝没有接受画家的报价,而是决定向他还价。然后画家可以接受这个还盘,其后果与之前一样:
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/counteroffer-acceptance.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=9923105b566068031bd08e1f6787c44d” id=“counteroffer-acceptance” className=“align-center” style={{width: “60.0%”}} alt=“原来的 PaintAgreement 流程图,但现在最上面的合约是 CounterOffer。“宽度=“600”高度=“363”数据路径=“images/docs_website/counteroffer-acceptance.svg”/>
在这里,通过创建
CounterOffer合约,Alice将她将IOU合约转让给画家的权利。如果是委托,在提交之前,请求者必须了解属于所请求交易一部分的合同,但请求者不是签署人。在上面的例子中,画家必须先知道Alice的借条的存在,然后才能请求接受CounterOffer。下一节中介绍的观察者和泄露的概念可以实现这种场景。 -
不能单方面逃避账本义务。一旦一项义务被记录在 Daml 账本上,就只能根据合同模型将其删除。例如,假设前面所示的 IOU 合约模型,如果账本记录了
MustPay合约的创建,那么银行以后就不能简单地记录消耗该合约的操作:<图片src =“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/validity-no-removal -of-obligations.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=8eb83f0989399fc8152a044a779fa839” className=“align-center” style={{width: “100.0%”}} alt=“一个时间序列,其中第一次提交包括创建 MustPay 合约,第二次提交包括使用该合约的银行,如上所述。“宽度=“1220”高度=“251”数据路径=“images/docs_website/validity-no-removal-of-obligations.svg”/>
也就是说,这个账本是无效的,因为上述行为不符合合约模型。{/* COPIED_START source=“docs-website:docs/replicated/daml/3.4/overview/explanations/ledger-model/ledger-privacy.rst” hash=“b3c9457c” */}
隐私
账本结构部分回答了“账本是什么样子?”的问题。通过引入分层格式来记录各方交互的变化。本节解决“谁看到哪些更改和数据?”的问题。也就是说,它解释了 Canton Ledgers 的隐私模型。
Canton Ledgers 的隐私模型基于需要知道的基础,并在子交易级别提供隐私。也就是说,一方仅了解影响该方拥有权益的合同的各方互动的那些部分,以及这些互动的后果。层次结构在这里很关键,因为它产生了子交易隐私的自然概念。为了使子交易隐私概念更加精确,我们引入了“被告知者”和“见证者”的概念。
知情人
一方可以在 Daml 模板和选择中扮演不同的角色;该方可以声明为signatory、选择controller、合同或选择observer。对于合同,如果一方是合同的签署人或合同观察员,则该方是利益相关者。
- 顾名思义,每个合同和选择
observer都应分别观察合同的更改(创建或存档)和选择的行使。 signatory受合约约束,因此拥有合约权益;他们应该了解合同何时创建或使用。- 练习的参与者,即所选择的
controller,在该动作中拥有利害关系,因此应该看到该练习;但他们可能在合同中没有利益。
这些观察结果引发了以下“被告知者”的定义,即应该被告知某项行动的一组各方。节点的信息对象是下表中标有 X 的集合的并集,
定义:节点的通知者是标有X的集合的并集。
例如,Create节点的信息接收者是所创建合约的利益相关者,即签署者和观察者。对于消费Exercise节点,信息接收者包括消费合约的利益相关者、行动的参与者和选择观察者。
作为一项设计决策,合约观察者不会被告知非消耗性的 Exercise 和 Fetch 操作,除非它们明确地位于参与者或选择观察者之中。这是因为此类操作不会改变合约本身的状态。
为了说明信息接收者的概念,我们使用 Alice 和 Bob 交换资产的运行示例。 AcceptAndSettle动作中的节点具有下图蓝色六边形所示的被通知者。例如,Alice 是根节点①的信息者,因为她是输入合约 #3 的签署人,而 Bob 是信息者,因为他是选择的参与者。类似地,Bank 2 和 Bob 是 Fetch 节点 ③ 的通知者,因为 Bank 2 是输入合约 #2 的签署人,而 Bob 是参与者。如果鲍勃不是参与者,他就不会成为信息接收者,因为合约观察者不会自动成为非消耗性练习和获取的信息接收者。<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-acceptandsettle-informees.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=deab88d50ec2ecdae62259b6fc20baa5” className=“align-center” style={{width: “100.0%”}} alt=“AcceptAndSettle 操作中节点的被通知者。” width=“2321” height=“1311” data-path=“images/docs_website/dvp-acceptandsettle-informees.svg” />
动作的被通知者是其根节点的被通知者。 Importantly, nodes cannot exist without their children on the Ledger, as mentioned in the ledger structure section;只有行动才能做到,因为它们是整棵树。因此,动作的被通知者有权查看该动作中的所有节点,即使他们不是某些单独节点本身的被通知者。这种差异在下一节的证人概念下得到了形式化。
见证人
单个节点可以是多个操作的一部分。例如,下图扩展了子操作图,每个子操作的无边框框的右上角显示了被通知者。 Here, the Create node ③ is part of three subactions, namely those rooted at nodes ①, ②, and ③.因此,该创建节点会向所有这些操作的通知者显示,即使他们不是节点本身的通知者。这些当事人被称为证人。形式上,对于给定的交易tx,tx中节点的见证人是包含该节点的tx所有子操作的通知者的并集。 In particular, every informee of the node is also a witness.
该图以紫色显示子操作的根操作的见证人。对于节点③,见证人是Alice、Bob和银行1,因为Bob是①和③的信息者;银行1是②、③的被告知人; Alice 是①、②的被告知者。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/dvp-settle-witness.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=fced7c0cefb8dc1176e5657ff6ce9e75” className=“align-center” style={{width: “60.0%”}} alt=“Settle选择的子动作的通知者和节点的见证者。“宽度=“1122”高度=“1082”数据路径=“images/docs_website/dvp-settle-witness.svg”/>
投影
知情者应该看到他们有权看到的更改,但这并不意味着他们有权看到包含此类更改的任何交易的全部内容。这是通过交易的“预测”来实现的,它定义了一组各方对交易的看法。直观上,给定提交内的交易,一组参与方只能看到包含合约上所有操作的子交易,其信息接收者至少包括其中一个参与方。 Equivalently, the group of parties sees only those nodes whose witnesses include at least one of the parties. Thus, privacy is obtained on the subtransaction level.
This section first defines projections for transactions and then for ledgers.
交易预测
下图给出了以AcceptAndSettleExercise操作作为唯一根操作的交易示例,其通知者如上图所示。
<图片src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-acceptandsettle-projection.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=19d1943bd83536d7d81d2d3748a81ef1” className=“align-center” style={{width: “100.0%”}} alt=“image” width=“1748” height=“3362” data-path=“images/docs_website/dvp-acceptandsettle-projection.svg” />
由于 Alice 和 Bob 都是根操作的信息接收者,即 Bob 对 Alice 的 ProposeSimpleDvP 合约行使 AcceptAndSettle 选择,因此对 Alice 或 Bob 或两者的投影包含整个“行使”操作。 As an action consists of the whole subtree underneath its root node, Alice and Bob each see all the nodes they are witnesses of. For example, Alice’s projection includes the Fetch subaction, Bob’s Transfer exercise of on #2, and the creation of Bob’s SimpleAsset contract #5.同样,Bob 的预测包括 Alice 的 Transfer 练习 #1 和 Alice 的 SimpleAsset 合约 #6 的创建。相比之下,银行“不是”根本行动的知情者。 In fact, Bank 1 appears as an informee only in the Transfer Exercise action on #1 and its subaction, the creation of Bob’s new asset #5.因此,对 Bank 1 的预测仅包含此练习操作。 Bank 2 显示为树中两个不相关操作的通知者:#2 上的 Fetch 操作和 Transfer 练习操作。因此,对银行 2 的投影包含以这两个操作作为根操作的交易。这表明投影可以将单个根操作转变为子操作列表。
底部对银行 1 和银行 2 的预测表明,对多方的预测可能比对每一方的预测包含的信息严格更多。换句话说,仅根据 Bank 1 的投影和 Bank 2 的投影来重建对 Bank 1 和 Bank 2 的投影是不可能的。这里,这是因为不能从各个投影中唯一地确定三个根动作的顺序。 For this reason, projection is defined for a set of parties.
一组 `P` 各方的交易的 **投影** 是通过对交易的每个根操作 `act` 执行以下操作获得的子交易。
1. If `P` contains at least one of the informees of `act`, keep `act` as-is, including its consequences.
2. Else, if `act` has consequences, replace `act` by the projection (for `P`) of its consequences, which might be empty.
3. Else, drop `act` including its consequences.
这个定义不是对节点进行操作,而是对动作进行操作,即节点的子树。因此,当且仅当P至少包含该节点的一个见证人时,一组参与方P的交易投影才包含该节点。
As a projection is a transaction, it is possible to project a projection further.投影操作具有以下吸收属性:对各方递减子集的投影是吸收的。也就是说,如果一组参与方P是Q的子集,那么首先将交易投影到Q,然后投影到P与直接将其投影到P相同。直观上,该属性表达了这样一个事实:一组各方共同了解的有关交易的信息至少与这些各方的任何子组自己了解的信息一样多。 The converse is false, as the above example with projection to Banks 1 and 2 has shown.
相反,如果P不是Q的子集,那么首先将交易投影到Q,然后投影到P,只会导致投影到P的子交易。例如,如果我们将银行 1 的上述投影投影给 Bob,则生成的交易仅包含合约 #5 的创建操作。 This is a proper subtransaction of Bob’s projection.
This difference reflects that Bank 1 learns less about the Exercise node than Bob.特别是,Bank 1 无法从其预测中推断出 Bob 是练习节点的见证人。 This is a general pattern: the informees of an action may not learn about witnesses of nodes therein. This is crucial from a privacy perspective as it hides who is involved in the hidden parts of the transaction.
账本投影
最后,一组 P 各方的分类账** l 的投影是更新的 DAG,如下所示:
- Project the transaction of each update in
lforP, but retain the update ID. - Remove updates with empty transactions from the result.
We defer defining the edges in the projection to the causality section. Until then, we pretend that the ledger is totally ordered and projections retain the same ordering.值得注意的是,账本的投影不是账本,而是更新的 DAG。 The requesters from the commit cannot be retained because they are typically witnesses of the actions in the projection, but not informees. Yet, the informees of the action must not know all the witnesses.例如,如果银行 1 的预测确实提到鲍勃是最后一次提交的请求者,那么银行 1 可以推断鲍勃是爱丽丝在合约 #1 上行使Transfer选择的见证人。
Projecting the ledger of the complete DvP example yields the following projections for each party.
<img src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/dvp-ledger-projections.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=9f32669cafa03a1f454985a745255eee” id=“da-ledgers-projections-example” className=“align-center” style={{width: “100.0%”}} alt=“各方投影的时间顺序,在下面的编号列表中深入解释。” width=“3802” height=“5682” data-path=“images/docs_website/dvp-ledger-projections.svg” />
Examine each party’s projection in turn:
- Alice sees all of the first, thrid, and forth commit as she is an informee of all root actions. In contrast, Alice does not see anything of the second commit, as she is not a stakeholder of Bob’s
SimpleAssetof 1 USD.这笔交易根本不存在于爱丽丝的投影中。 Yet, the output of this transaction (contract #2) is used in the last commit of Alice’s projection.因此,合约 #2 显示为分类账外部左侧的输入。这种影响将在下面的追溯披露中讨论。 - Bob 的投影类似于 Alice 的投影:他看到了第二次、第三次和第四次提交的所有内容,但没有看到第一次提交的任何内容,而只是将合约 #1 作为输入。
- Bank 1 和 2 只能看到它们创建
SimpleAsset和Transfer练习的提交。此外,银行 2 在最后一次提交中看到了SimpleAsset的获取,正如上面针对事务预测所讨论的那样。
The update IDs enable correlation across the different projections.例如,银行 1 和银行 2 都看到更新 ID TX 3。因此,他们可以推断他们的投影发生在同一个原子事务中,即使他们的投影不共享单个节点。
Divulgence: When non-stakeholders see contracts
Canton账本隐私模型的指导原则是合同只能向利益相关者展示。然而,账本预测也可能导致合同对其他方可见。 Showing contracts to non-stakeholders through ledger projections is called divulgence.泄露是 Canton Ledgers 设计中经过深思熟虑的选择,有两种形式:
-
Immediate divulgence refers to witnesses seeing contract creations they are not an informee of.在 DvP 的账本投影示例中,Bob 是 Alice 的新
SimpleAsset(合约 #6)的创建操作的见证人,但不是信息接收者。从概念上讲,在鲍勃执行Transfer选择的那一刻,他也获得了Transfer结果的临时权益,即看到该资产现在属于Alice。一般来说,隐藏行动的后果是没有意义的。无论如何,Bob 都可以计算它所了解的行为的后果,因为 Daml 是确定性的。
-
Retroactive divulgence refers to an input contract being shown to the non-informee witnesses of a node using this contract.例如,Bob 的
SimpleAsset(合约 #2)上的 Fetch 对 Alice 和 Alice 的投影可见,因此即使 #2 的创建操作不是 Alice 投影的一部分,也会引用此合约作为输入。追溯泄露使 Alice 能够验证她预测中的交易(有关账本完整性,请参阅
da-model-consistency)。也就是说,Alice 可以检查 Bob 是否根据她在提案中指定的内容分配了合适的SimpleAsset。追溯泄露不会使 Alice 成为 Bob
SimpleAsset(合约 #2)的 Create 操作的见证人,因为输入合约与其 Create 操作不同。在图中,这种区别通过输入合约的虚线边框可视化,并将它们放置在左侧。通过 Ledger API 的更新服务,用户可以在各方投影的树中看到立即泄露的合约,因为这些树包含创建节点。 In contrast, the Ledger API currently does not offer a means for a user to look up a contract ID of a retroactive divulgence.
披露:当非利益相关者使用合约时
上一节的泄露是指各方了解他们不是利益相关者的合同。披露是指此类各方在自己的交易中使用合同的情况。
Recall from the running example that Bob uses submitWithDisclosures for the exercising Settle choice. This is because Bob (and its 参与方节点) in general does not know about the SimpleAsset contract #2 that Alice has allocated to the proposal. Disclosure means that Alice tells Bob via an off-ledger communication channel about this contract.在Daml脚本运行示例中,脚本本身就是通信通道。 In real-world contexts, Alice would offer an API for Bob to retrieve the relevant data.
It is a design decision that immediate divulgence does not entail disclosure for subsequent transactions. For example, after the DvP has been settled, Alice creates another DvP proposal for Bob to swap the two assets again:
proposeDvP2 <- submit alice $ do
createCmd ProposeSimpleDvP with
proposer = alice
counterparty = bob
allocated = newUsd
expected = eurAsset
Then, Bob’s command submission must include the disclosure of Alice’s SimpleAsset even though Bob is a witness of the creation of Alice’s SimpleAsset. A plain submit without disclosure does not work.
不使用立即披露隐式披露的动机是它会导致脆弱的工作流程。问题在于,非利益相关者只了解合约的创建,而不了解合约的后续操作(例如存档)。 Accordingly, there is no general rule as to how long the non-stakeholder should long to keep the contract around.存放时间过长会浪费存储空间;保持太短可能会破坏某些应用程序。相反,该规则迫使应用程序明确设计披露(即使是泄露的合同),并提出合适的特定于应用程序的规则。
An alternative approach to disclosure is to replace the original SimpleAsset contract by one on which the Bob becomes a contract observer.这需要通过在 SimpleAsset 上进行(消耗)行使操作来扩展合约模型,从而创建一个新的 SimpleAsset,并由 Alice 选择的观察者。除了增加账本上的操作之外,这两种方法的不同之处在于谁了解了解合同的各方:
- If Alice discloses her
SimpleAssetto Bob via an off-ledger channel, only Alice and Bob need to know about this disclosure.因此,当爱丽丝向查理披露同一份合约时,查理不需要知道爱丽丝已经向鲍勃展示了该合约,而鲍勃也不需要知道爱丽丝正在向查理披露该合约。 - 相反,当 Alice 将 Bob 添加为合约观察者,然后将 Charlie 添加为另一个观察者时,作为合约观察者的 Bob 会收到有关存档和创建的通知。 Similarly, Charlie learns that Bob is an observer on the contract, too. That is, all stakeholders learn about each other.当爱丽丝实际上不希望鲍勃和查理知道彼此时,这就产生了隐私问题。
此外,将参与方添加为观察者很难扩展到大量观察者,因为每个观察者都会了解其他观察者:具有 N 观察者的创建事件至少会出现在那些 N 参与方的投影中。因此,所有投影的大小在N中已经是二次方,因为至少N大小的动作出现在N不同的投影中。如果观察者一一相加,则需要N档案和创作,这意味着所有投影加起来的大小在N中是立方体。
{/* COPIED_START source=“docs-website:docs/replicated/daml/3.4/overview/explanations/ledger-model/interoperability.rst” hash=“a2bb1cc4” */}
同步器感知投影
当一方托管在多个参与者节点上时,互操作性可能会限制参与者节点对一方的账本投影(即其本地账本)的可见性。这些限制影响各方可以通过每个参与者节点的 Ledger API 观察到的内容。特别是,互操作性会影响一方观察哪些事件及其顺序。本文档通过示例和正式介绍互操作版本的因果关系图和投影来解释由于互操作性造成的可见性限制及其对更新服务的影响。
本演示假定您熟悉以下概念:
- 账本 API
- Daml 账本模型
- 本地账本和因果关系图
互操作性示例
拓扑
参与者节点连接到 Daml 账本,各方通过 Ledger API 访问这些账本的投影。下图显示了这样的设置。
<图>
该图中的组件如下:
- 有一组可互操作的 Daml 账本:Ledger 1(绿色)和 Ledger 2(黄色)。
- 每个 参与者节点 都连接到 Daml 账本的一个子集。
- 参与者节点 1 和 3 连接到账本 1 和 2。
- 参与者节点 2 仅连接到账本 1。
- 参与者节点在它们所连接的 Daml 账本子集上托管各方。参与者节点为一方提供对其托管该方的 Daml 账本的访问权限。
- 参与者节点 1 在账本 1 和 2 上托管 Alice。
- 参与者节点 2 在账本 1 上托管 Alice。
- 参与者节点 3 托管 Ledger 1 和 2 上的画家。
在参与者处聚合
参与者节点从这些账本中收集更新,并通过参与方的更新服务和状态服务输出它们。当参与者节点仅在可互操作的 Daml 分类账的子集上托管一方时,则参与者节点的更新和状态服务仅从这些分类账中派生。
例如,在上面的拓扑中,当交易在 Ledger 2 上与利益相关者 Alice 创建合约时,Alice 的 P1 交易流将发出此交易并将合约报告为活动状态,但 Alice 的 P2 交易流不会。
进入和离开活动
通过互操作性,交易可以使用其创建记录在不同分类账上的合约。在上面的拓扑中,例如,一笔交易在 Ledger 1 上与利益相关者 Alice 创建了一份合约 c1,另一笔交易在 Ledger 2 上存档该合约。然后参与者节点P2 将 Create 操作作为 CreatedEvent 输出,但不会在更新服务上以 ArchiveEvent 的形式输出 Exercise,因为 Ledger 2 无法通知P2 因为 P2 不在 Ledger 2 上托管 Alice。相反,当一笔交易在 Ledger 2 上与利益相关者 Alice 创建合约c2,而另一笔交易在 Ledger 1 上存档该合约时,P2 输出 ArchivedEvent,但不输出 CreatedEvent。为了保持交易流一致,P2还对Alice的交易流输出离开c1操作。该动作表明参与者节点不再输出与该合约相关的事件;特别是在合同存档时。因此,该合同不再在国家服务部门报告,也不能被指挥部提交使用。
相反,P2 在事务流上的 ArchivedEvent 之前某个时间输出 Enter c2 操作。此操作表明参与者节点开始输出与此合约相关的事件。该合同在国家服务部门报告,并且可以通过命令提交来使用。
Enter 和 Leave 操作分别类似于 Create 和消耗 Exercise 操作,不同之处在于,Enter 和 Leave 对于同一个合约可能会发生多次,而每个合约最多应有 1 个 Create 操作和最多 1 个消耗 Exercise 操作。
这些进入和离开事件是由底层互操作性协议生成的。这可能作为命令提交的一部分或出于其他原因(例如负载平衡)而发生。根据底层账本和互操作性协议的信任假设,保证输入操作先于合约使用。
合约可以多次进入和离开参与者节点的可见性。例如,假设画家提交了以下命令,并且他们的提交最终出现在给定的账本上。
- 在 Ledger 2 上与 Alice 和画家签订合同
c - 在 Ledger 1 的
c上执行非消耗选择ch1。 - 在 Ledger 2 上的
c上执行非消耗选择ch2。 - 在 Ledger 1 上的
c上执行消费选择ch3。
那么,P2为A提供的交易树流包含涉及合约`c`的五个动作:进入、非消耗行使、离开、进入、消耗行使。重要的是,P2 一定不能省略 Leave 操作和随后的 Enter,即使它们看起来相互抵消。这是因为它们的存在表明P2的Alice事件流可能会错过中间的一些事件;在此示例中,执行选择 ch2。
P2的扁平交易流省略了非消耗性的锻炼选择。尽管如此,它在消费练习之前包含三个动作进入、离开、进入。这是因为参与者节点在离开操作时无法知道将会有另一个进入操作到来。
相反,在此示例中,P1 根本不需要输出 Enter 和 Leave 操作,因为 P1 在两个账本上都托管 Alice。
跨账本交易
通过互操作性,跨账本交易可以同时在多个可互操作的 Daml 账本上提交。这样的跨账本交易避免了 Enter 和 Leave 操作的一些同步开销。当跨账本交易使用来自多个 Daml 账本的合约时,利益相关者可能会见证其合约上的操作,而这些操作实际上在参与者节点上不可见。
例如,假设因果关系示例中的分割油漆还价工作流程按如下方式提交:CounterOffer和PaintAgree合约上的操作均在账本 1 上提交。Iou上的所有操作均在账本 2 上提交,假设某些参与者节点在账本 2 上托管银行。最后一笔交易是跨账本交易,因为CounterOffer 和PaintAgreement 的创建在 Ledger 1 上提交,同时将 Alice 的 Iou 转移给 Ledger 2 上的画家。
对于最后一笔交易,参与者节点 1 像往常一样通过更新服务向 Alice 通知交易树、两个档案和PaintAgree 创建。参与者节点2还在Alice的交易树流上输出整个交易树,其中包含Alice的Iou的消费Exercise。但是,它尚未输出 Alice 的 Iou 的 Create,因为 Iou 操作在 Ledger 2 上提交,而 Ledger 2 上的参与者节点 2 不托管 Alice。因此,爱丽丝只是“见证”档案,尽管她是该练习的知情者。因此,练习操作被标记为仅在参与者节点 2 的事务树流上被见证。一般来说,当一方是该行动的知情者时,该行动被标记为“仅被见证”,但该行动并未在参与者节点托管该方的账本上提交。与进入和离开不同,从参与者的角度来看,这种见证的行为不会影响因果关系,因此提供较弱的排序保证。这种目击行为既不会出现在统一交易流中,也不会出现在国家服务中。
例如,假设 Create PaintAgree 操作在 Ledger 2 上提交,而不是在 Ledger 1 上提交,即只有 CounterOffer 操作在 Ledger 1 上提交。然后,参与者节点 2 将 Create PaintAgree 操作标记为仅在事务树流上见证。因此,它不会将合约报告为活动状态,Alice 也无法通过参与者节点 2 在她的提交中使用该合约。
多账本因果关系图
本节将因果关系图概括为互操作性设置。
每份活跃的 Daml 合约最多驻留在一个 Daml 账本上。对合同的任何使用都必须在其所在的 Daml 分类账上进行。最初,当合约创建时,它会驻留在 Daml 账本上,并在该账本上提交 Create 操作。为了使用驻留在不同 Daml 分类账上的合约,跨分类账交易需要在多个 Daml 分类账上提交。
然而,跨账本交易会产生开销,如果合约经常在非其驻留的 Daml 账本上使用,则互操作协议可以将合约迁移到另一个 Daml 账本。合约放弃在原Daml账本上的驻留并在目标Daml账本上驻留的过程称为合约转移。交易流上的 Enter 和 Leave 事件源自此类合约传输,如下所述。此外,合约传输是源 Daml 账本和目标 Daml 账本之间的同步点,因此会影响订购保证。因此,我们概括了因果关系图以实现互操作性。
定义“转移动作”
合约c上的转移动作写为转移c。转移行动的知情者是c的利益相关者。
在下文中,术语“操作”指的是交易操作(Create、Exercise、Fetch 和 NoSuchKey)以及转账操作。特别是,对合约c的转账操作是对c的操作。但转账操作不会出现在交易中。因此,事务操作不能因此产生转移操作,并且转移操作根本不产生结果。
定义»多账本因果关系图«
Daml 账本集合 Y 的 多账本因果关系图 G 是有限的、传递闭合的、有向无环图。顶点要么是交易,要么是传输动作。根据下表,每个操作都可能用来自 Y 的 传入分类帐 和 传出分类帐 进行注释:
| 行动 | 传入分类帐 | 传出分类帐 |
|---|---|---|
| 创建 | 没有 | 是的 |
| 消耗锻炼 | 是的 | 没有 |
| 非消耗锻炼 | 是的 | 是的 |
| 获取 | 是的 | 是的 |
| NoSuchKey | 没有 | 没有 |
| 转让 | 也许 | 也许 |
对于非消耗性的 Exercise 和 Fetch 操作,传入账本必须与传出账本相同。 传输操作必须至少有其中之一。具有两组的传输操作代表完整的传输。如果只设置了传入账本,则代表进入事件的部分信息;如果只设置了外出,则为Leave事件的部分信息。缺少传入或传出分类帐注释的转移操作分别称为输入或离开操作。
行动顺序相应地推广到多账本因果关系图。在 Enter 和 Leave 事件的示例中,画家与签名者 Alice 和画家对合同c 执行了三个选择,这四笔交易产生了以下多账本因果关系图。传入和传出的账本被编码为颜色(账本 1 为绿色,账本 2 为黄色)。 传输顶点显示为圆圈,其中左半部分用传入分类帐着色,右半部分用传出分类帐着色。
<figcaption>具有转移操作的多账本因果关系图</figcaption>
</图>
作为跨域交易的示例,请考虑跨域交易的拆分油漆还价工作流程。对应的多账本因果关系图如下所示。最后一笔交易tx4是跨账本交易,因为它的操作有不止一种颜色。
<figcaption>两个 Daml 账本上的分割油漆还价工作流程的多账本因果关系图</figcaption>
</图>
一致性
定义»账本追踪«
账本追踪是 (a``i``, b``i``) 的有限列表,使得 b``i - 1 = a``i 对于所有 i > 0。 a``i和b``i标识Daml账本或者是特殊值NONE,与所有Daml账本标识符不同。
定义»合同的多账本因果一致性«
令 G 为多账本因果关系图,X 为 G 对 c 合约的一组操作。如果满足以下所有条件,则图表 G 对于合约来说是多账本一致的 c 在 X 上:
- 如果
X不为空,则X包含一个Create或至少一个Enter操作。如果它包含创建,则此创建优先于X中的所有其他操作。如果不存在,则存在一个 Enter 操作,该操作先于X中的所有其他操作。 X最多包含一个创建动作。- 如果
X包含消耗性练习操作act,则act遵循G的操作顺序中X中的所有其他操作。 X中的所有 传输 操作均与X中的所有其他操作一起排序。- 对于
X(即X的最大全序子集)中的每个最大链,(传入账本、传出账本)对的序列是账本跟踪,如果该操作没有传入或传出账本注释,则使用NONE。
前三个条件模拟普通因果图的因果一致性条件。他们确保创建操作首先出现,最后消耗锻炼操作。如果没有“创建”,则“输入”操作将充当“创建”的角色。第四个条件确保所有转账动作都是合约的同步点。关于账本追踪的最后一个条件确保合约仅驻留在一个 Daml 账本上,并且所有使用都发生在居住的账本上。特别是,离开之后的下一个合约操作必须是进入。
例如,上面带有转移操作的多账本因果关系图对于c来说是多账本一致的。特别地,c上的动作只有一条最大链,即
创建
c->tf1-> ExeNBcch1->tf2-> ExeNBcch2->tf3-> ExeNBcch3,对于每条边act``1->act``2,act``1的传出账本颜色与act``2的传入账本颜色相同。对最大链的限制确保不会跳过任何节点。例如,(非最大)链
创建
c-> ExeNBcch1->tf2-> ExeNBcch2->tf3-> ExeBcch3
不是分类帐跟踪,因为 Create 操作(黄色)的传出分类帐与ch1 的非消耗 Exercise 操作(绿色)的传入分类帐不同。因此,没有tf1顶点的子图对于c来说不是多账本一致性的,即使它是一个多账本因果关系图。
定义»多账本因果关系图的一致性«
令 X 为多账本因果关系图中的操作子集G。如果 G 对于所有合约 c 对于X 中c 的操作集是多账本一致的,则G 对于X 是多账本一致(或X-多账本一致)。如果 G 在 G 中的所有操作都是多账本一致的,则 G 是多账本一致的。
分割油漆还价工作流程的多账本因果关系图是多账本一致的。特别是合约上的所有最大操作链都是账本跟踪:
| 合同 | 最大链 |
|---|---|
Iou Bank A | 创建 -> 获取 -> 练习 |
ShowIou A P Bank | 创建 -> 练习 |
Counteroffer A P Bank | 创建 -> 练习 |
Iou Bank P | 创建 |
PaintAgree P A | 创建 |
极简和减少
当将边添加到X-多账本一致性因果关系图中,使其保持非循环且传递闭合时,生成的图再次是X-多账本一致性。因此,最低限度一致和约简的概念是从普通因果图相应地概括出来的。
定义»最小多账本一致因果关系图«
如果G没有严格的子图(相同的顶点,更少的边)是X-多账本一致性因果关系图,则X-多账本一致性因果关系图G是X-最小。如果X是G中所有动作的集合,则X被省略。
定义»减少多账本一致因果关系图«
对于X-多账本一致因果关系图G,存在唯一的最小X-多账本一致因果关系图reduce``X``(G),其顶点相同且边是G的子集。 reduce``X``(G)称为G的X-还原。和之前一样,如果X包含G中的所有动作,则省略它。
由于多账本因果关系图是非循环的,因此可以对它们的顶点进行拓扑排序,并且结果列表又是一个因果关系图,其中每个顶点都有到所有后续顶点的传出边。如果原始因果图是X一致的,那么拓扑排序也是如此,因为拓扑排序只是添加边。
从多账本因果关系图到账本
多账本因果关系图G通过拓扑排序和归约链接到Daml账本模型中的账本L。
- 给定一个多账本因果关系图
G,删除传入和传出账本注释以及所有传输顶点,对交易顶点进行拓扑排序,并与请求者扩展交易结果列表以获得提交序列L。 - 给定一系列提交
L,使用事务作为顶点,并在序列中每当tx1的提交先于tx2的提交时添加从tx1到tx2的边。然后根据需要添加传输顶点以及传入和传出账本注释,并将它们用边连接到交易顶点。此链接仅在某种程度上保持一致性。也就是说,如果多账本因果关系图对于合约c是多账本一致的,那么相应的账本对于合约c也是一致的。然而,多账本一致的因果关系图不会产生一致的账本,因为可能会违反密钥一致性。相反,一致的账本不会谈论传入和传出的账本注释,因此不能强制注释一致。
账本感知投影
参与者节点为其托管的每一方维护一个本地分类账,更新服务输出该本地分类账的拓扑排序。当参与者节点在多个账本上托管该方时,该本地账本是一个多账本因果关系图。本节定义了多账本因果关系图的账本感知投影,从而产生这样一个本地账本。
定义“Y 标记动作”
如果传入或传出分类帐注释是Y 的元素,则具有传入和传出分类帐注释的操作对于集合 Y 具有 Y 标签。
定义“账本感知交易预测”
令Y是一组Daml账本,tx是一个交易,其操作用传入和传出账本进行注释。令 Act 为 tx 的Y 标记子动作集合,P 是其中的通知者。 tx P 在 Y 上的 账本感知投影(P-**Y 上的投影)由按执行顺序排列的 Act(w.r.t. 子动作关系)的所有最大元素组成。
例如,分割油漆还价工作流程中的跨域交易,爱丽丝和画家在Iou账本(黄色)和绘画账本(绿色)上有以下投影。这里,绿色账本上的投影包括黄色账本的动作,因为投影包括子动作。
<图片src =“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/projecting-transactions-pa int-offer-ledger-aware.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=9545c405bf28aaf0d8098af837f2e42a” className=“align-center” style={{width: “60.0%”}} alt=“分割油漆还价工作流程中各方的投影。绿色账本投影包括黄色账本,但黄色账本投影不包括绿色账本。“宽度=“660”高度=“1100”数据路径=“images/docs_website/projecting-transactions-paint-offer-ledger-aware.svg”/>
定义“转移动作的预测”
令 act 为带有传入分类帐和/或传出分类帐注释的转移操作。 act 在一组账本 Y 上的 投影 会删除 act 中不在 Y 中的注释。如果投影删除所有注释,则它是空的。
如果P是合约的利益相关者,act对P在Y上的投影(P-在Y上的投影)是act在Y上的投影,否则为空。
定义»一方的多账本一致性«
如果 G 在一组 Y 标记的操作上是多账本一致的,则多账本因果关系图 G 对于一方来说一致 P 在一组账本 Y 上(P-在 Y 上一致)在G中,其中P是利益相关者知情者。
X-极小性和X-减少的概念相应地扩展到一组Y账本上的一方P。
定义 »多账本因果关系图的账本感知投影«
令 G 为多账本一致性因果关系图,Y 为一组 Daml 账本。 G在Y上对P的投影(P-在Y上的投影)是以下因果图G'在Y上的P约简,即P-与`Y`一致:
G'的顶点是G在Y上投影到P的顶点,不包括空投影。- 如果从
v``1对应的G-顶点到G-顶点有一条边,则G'中的两个顶点v``1和v``2之间有一条边对应v``2。
如果 G 是多账本一致性因果关系图,那么 Y 上的 P 投影在 Y 上也是P 一致的。例如,分割油漆还价工作流程的多账本因果关系图投影如下:
<图片src=“https://mintcdn.com/cantonfoundation/QAGFSphBsRkeZIBi/images/docs_website/counteroffer-causality-ledgeraware-projection.svg?fit=max&auto=format&n=QAGFSphBsRkeZIBi&q=85&s=56332b2eb0f3b2c26fdf386b3b4fb024” className=“align-center” style={{width: “100.0%”}} alt=“分割油漆还价工作流程中各方的更多投影,显示更多细节。爱丽丝和画家有绿色和黄色,只有绿色和黄色投影;银行只有黄色投影。“宽度=“1452”高度=“2389”数据路径=“images/docs_website/counteroffer-causality-lederaware-projection.svg”/>
以下几点值得强调:
- 在爱丽丝在绿色账本上的投影中,爱丽丝见证了她的
Iou的档案。正如下面interop-ordering-guarantees中所解释的,锻炼操作被标记为仅在绿色分类账上托管爱丽丝的参与者节点的交易流中被见证,但不在黄色分类账上。同样,画家只是在绿色账本上的画家投影中见证了他的Iou的创建。 - 在 Painter 的预测中,
ShowIou交易tx3是无序的。到tx4中的CounterOffer接受,就像普通因果图的情况一样。边缘tx3->tx4在投影过程中通过缩减步骤被移除。
转移动作的投影可以用interoperable-causality-graph-linear来说明。黄色和绿色账本上的 A 投影如下所示。白色表示转账操作没有传入或传出账本注释。也就是说,“离开”操作在右侧为白色,“进入”操作在左侧为白色。
<img src=“https://mintcdn.com/cantonfoundation/zmlOjLpKuDjnaObr/images/docs_website/transfer-projection.svg?fit=max&auto=format&n=zmlOjLpKuDjnaObr&q=85&s=08e1311acdd7eadfa8f1cd145f8c6ccc” className=“align-center” style={{width: “100.0%”}} alt=“因果关系图仅显示绿色或仅显示黄色分类帐。“宽度=“1464”高度=“451”数据路径=“images/docs_website/transfer-projection.svg”/>
Ledger API 订购保证
更新服务和状态服务源自参与者节点为参与方维护的本地分类账。令 Y 为参与者节点托管一方的账本集合。交易树流在 Y 上输出该方本地账本的拓扑排序,并进行以下修改:
- 带有传入或传出账本注释的 转账 操作将输出为 Enter 和 Leave 事件。带有传入和传出分类帐注释的 转移 操作被省略。
- 不输出进出账本注释。如果该方是该操作的通知者,则具有不在
Y中的传入或传出分类帐注释的交易操作将被标记为仅被见证。 3.省略Fetch节点和NoSuchKey。
扁平交易流精确地包含CreatedEvents、ArchivedEvents以及Enter和Leave操作,这些操作对应于交易树流上交易树中的Create、消耗Exercise、Enter和Leave操作,其中一方是受影响合约的利益相关者,并且不被标记为仅仅被见证。
类似地,状态服务根据扁平交易流提供在返回的偏移量处处于活动状态的一组合约。也就是说,在提供的合约集中考虑了交易事件流中所有事件的合约状态变化。
单个 Daml 分类账的订购保证相应延长。特别是,互操作性确保所有本地账本都是连接到如上所述的 Daml 账本模型的虚拟共享多账本因果关系图的投影。因此,账本有效性保证通过本地账本扩展到 Ledger API。
本文由 CC Privacy Club 根据 Canton Network 官方文档(CC-BY-4.0)整理翻译,仅供学习;实现细节以官方最新版本为准。