应用程序开发 - 应用程序设计元素 - 链码命名空间
链码命名空间允许它保持其世界状态与其他链码分离。具体来说,具有相同链码的智能合约共享对同一世界状态的直接访问,而具有不同链码的智能合约不能直接访问彼此的世界状态。如果智能合约需要访问另一个链码世界状态,则可以通过执行链码到链码的调用来做到这一点。最后,区块链可以包含与不同世界状态相关的交易。
在本主题中,我们将介绍:
- 命名空间的重要性
- 什么是链码命名空间
- 通道和名称空间
- 如何使用链码命名空间
- 如何通过智能合约访问世界状态
- 链码命名空间的设计注意事项
1. 动机
命名空间是一个常见的概念。我们知道,纽约的公园街和西雅图的公园街虽然名称相同,但却是不同的街道。这个城市形成了公园街的命名空间,同时提供了自由和清晰。
在计算机系统中也是一样。命名空间允许不同的用户对共享系统的不同部分进行编程和操作,而不会互相干扰。许多编程语言都有命名空间,因此程序可以自由分配唯一的标识符,例如变量名,而不必担心其他程序会这样做。我们将看到 Hyperledger Fabric 使用命名空间来帮助智能合约将其帐本世界状态与其他智能合约区分开。
2. 场景
让我们使用下图检查账本世界状态如何组织对组织中重要组织的业务对象的事实。无论这些对象是商业票据,债券还是车辆登记证,以及它们在其生命周期中的任何位置,都将其维护为帐本世界状态数据库中的状态。智能合约通过与帐本 (世界状态和区块链) 进行交互来管理这些业务对象,在大多数情况下,这将涉及查询或更新帐本世界状态。
了解帐本世界状态是根据访问它的智能合约的链码进行分区的,这一点至关重要,对于架构师,管理员和程序员来说,这种分区或命名空间是重要的设计考虑因素。
账本世界状态根据访问它的链码分为不同的命名空间。在给定的通道内,相同链码中的智能合约共享相同的世界状态,而不同链码中的智能合约无法直接访问彼此的世界状态。同样,区块链可以包含与不同链码世界状态相关的交易。
在我们的示例中,我们可以看到在两个不同的链码中定义了四个智能合约,每个合约都在自己的链码容器中。 euroPaper 和 yenPaper 智能合约在 papers
链码中定义。对于 EuroBond 和 JPYBond 智能合约,情况也是如此 - 它们在 bonds
链码中定义。这种设计可以帮助应用程序程序员了解他们是在使用欧元还是日元定价的商业票据还是债券,并且由于每种金融产品的规则对于不同的货币并没有真正改变,因此有必要使用相同的链码来管理其部署。
该图 还显示了此部署选择的后果。数据库管理系统 (DBMS) 为 papers
和 bonds
链码以及其中包含的智能合约创建不同的世界状态数据库。世界状态 A 和世界状态 B 分别保存在不同的数据库中;数据彼此隔离,因此单个世界状态查询 (例如) 无法访问两个世界状态。据说世界状态根据其链码进行了命名。
了解世界状态 A 如何包含两个商业票据列表 paperListEuro
和 paperListYen
。状态 PAP11 和 PAP21 是分别由 euroPaper 和 yenPaper 智能合约管理的每种票据的实例。因为它们共享相同的链码命名空间,所以它们的键 (PAPxyz) 在 papers
链码的命名空间内必须是唯一的,有点像街道名称在城镇中是唯一的。注意,有可能在 papers
链码中编写智能合约,从而对所有商业票据 (无论是欧元还是日元) 进行汇总计算,因为它们共享相同的命名空间。债券的情况与此类似 - 它们保存在世界状态 B 中,该状态映射到单独的债券数据库,并且其键必须唯一。
同样重要的是,命名空间意味着 euroPaper 和 yenPaper 无法直接访问世界状态 B,而 EuroBond 和 yenBond 无法直接访问世界状态 A。这种隔离非常有用,因为商业票据和债券是非常不同的金融工具;它们具有不同的属性,并遵循不同的规则。这也意味着票据和证券可以具有相同的键,因为它们位于不同的命名空间中。这很有帮助;它为命名提供了很大的自由度。使用这种自由来有意义地命名不同的业务对象。
最重要的是,我们可以看到区块链与在特定通道中运行的对端节点相关联,并且它包含影响世界状态 A 和世界状态 B 的交易。这是因为区块链是对端节点中包含的最基本的数据结构。可以始终从此区块链重新创建世界状态集,因为它们是区块链交易的累积结果。世界状态有助于简化智能合约并提高其效率,因为它们通常仅需要一个状态的当前值。通过命名空间将世界状态分开可以帮助智能合约将其逻辑与其他智能合约隔离,而不必担心对应于不同世界状态的交易。例如,债券合约不需要担心票据交易,因为它看不到它们产生的世界状态。
还值得注意的是,对端节点,链码容器和 DBMS 在逻辑上都是不同的进程。对端节点及其所有链码容器始终在物理上独立的操作系统进程中,但是根据其 类型,可以将 DBMS 配置为嵌入式或独立。对于 LevelDB,DBMS 完全包含在对端节点中,但是对于 CouchDB,它是一个单独的操作系统进程。
重要的是要记住,在此示例中,命名空间的选择是业务要求的结果,该业务要求共享不同货币的商业票据,但将它们与债券分开。考虑如何修改命名空间结构以满足业务需求,以使每个金融资产类别保持独立,或共享所有商业票据和债券?
3. 通道
如果对端节点加入了多个通道,则会为每个通道创建并管理一个新的区块链。而且,每次在新的通道中实例化一个链码时,都会为其创建一个新的世界状态数据库。这意味着通道还与世界状态的链码一起形成一种命名空间。
但是,相同的对端节点和链码容器进程可以同时加入多个通道 – 与区块链和世界状态数据库不同,这些进程不会随着加入的通道数量而增加。
例如,如果票据和债券链码在新的通道上实例化,则将创建一个完全独立的区块链,并创建两个新的世界状态数据库。但是,对端节点和链码容器不会增加;每个都将连接到多个通道。
4. 使用
让我们使用我们的商业票据 示例 来说明应用程序如何使用带有命名空间的智能合约。值得注意的是,应用程序与对端节点通信,并且对端节点将请求路由到适当的链码容器,然后容器可以访问 DBMS。该路由由图中所示的对端节点核心组件完成。
这是使用商业票据和债券的应用程序代码,以欧元和日元定价。该代码是不言自明的:
const euroPaper = network.getContract(papers, euroPaper);
paper1 = euroPaper.submit(issue, PAP11);
const yenPaper = network.getContract(papers, yenPaper);
paper2 = yenPaper.submit(redeem, PAP21);
const euroBond = network.getContract(bonds, euroBond);
bond1 = euroBond.submit(buy, BON31);
const yenBond = network.getContract(bonds, yenBond);
bond2 = yenBond.submit(sell, BON41);
查看应用程序如何:
- 使用指定
papers
链码的getContract()
API访问 euroPaper 和 yenPaper 合约。请参阅交互点 1a 和 2a。 - 使用指定
bonds
链码的getContract()
API 访问 euroBond 和 yenBond 合约。参见交互点 3a 和 4a。 - 使用 euroPaper 合约向商业票据 PAP11 的网络提交
issue
交易。参见相互作用点 1a。这导致在世界状态 A 中以状态 PAP11 表示的商业票据的创建;互动点 1b。此操作在交互点 1c 被捕获为区块链中的交易。 - 使用 yenPaper 合约向网络提交商业票据 PAP21 的赎回交易。请参阅交互点 2a。这导致在世界状态 A 中以状态 PAP21 表示的商业票据的创建;互动点 2b。该操作在交互点 2c 被捕获为区块链中的交易。
- 使用 euroBond 合约向债券网络 BON31 提交
buy
交易。参见相互作用点 3a。这导致在世界状态 B 中以状态 BON31 表示的键的创建;互动点 3b。该操作在交互点 3c 被捕获为区块链中的交易。 - 使用日元债券合约向网络提交
sell
交易以获取债券 BON41。参见相互作用点 4a。这导致在世界状态 B 中以状态 BON41 表示的键的创建;互动点 4b。该操作在交互点 4c 被捕获为区块链中的交易。
了解智能合约如何与世界状态交互:
- euroPaper 和 yenPaper 合约可以直接访问世界状态 A,但是不能直接访问世界状态 B。世界状态 A 物理上保存在与票据链码相对应的数据库管理系统 (DBMS) 中的票据数据库中。
- euroBond 和 yenBond 合约可以直接访问世界状态 B,但不能直接访问世界状态 A。世界状态 B 物理上保存在与债券链码相对应的数据库管理系统 (DBMS) 的债券数据库中。
了解区块链如何捕获所有世界状态的交易:
- 交互 1c 和 2c 分别对应于创建和更新商业票据 PAP11 和 PAP21 的交易。这些都包含在世界状态 A 中。
- 交互 3c 和 4c 对应于更新债券 BON31 和 BON41 的交易。这些都包含在世界状态 B 中。
- 如果世界状态 A 或世界状态 B 由于任何原因被破坏,则可以通过重放区块链中的所有交易来重新创建它们。
5. 跨链码访问
正如我们在示例 场景 中看到的那样,euroPaper 和 yenPaper 无法直接访问世界状态 B。这是因为我们已经设计了链码和智能合约,因此这些链码和世界状态彼此分开保存。但是,让我们假设 euroPaper 需要访问世界状态 B。
为什么会发生这种情况?想象一下,当发行商业票据时,智能合约想要根据具有相似到期日的债券当前收益对票据定价。在这种情况下,euroPaper 合约必须能够查询处于世界状态 B 的债券的价格。请查看下图,看看我们如何构建这种交互。
链码和智能合约如何通过其链码间接访问另一个世界状态。
注意:
- 该应用程序在 euroPaper 智能合约中提交发行交易以发行 PAP11。参见互动 1a。
- euroPaper 智能合约中的发行交易调用 euroBond 智能合约中的查询交易。参见相互作用点 1b。
- euroBond 中的查询可以从世界状态 B 中检索信息。请参见交互点 1c。
- 当控制权返回到发行交易时,它可以使用响应中的信息对票据定价,并使用信息更新世界状态 A。参见相互作用点 1d。
- 发行以日元计价的商业票据的控制流程相同。参见交互点 2a,2b,2c 和 2d。
使用 invokeChaincode()
API 在链码之间传递控制。该 API 将控制权从一个链码传递到另一个链码。
尽管我们仅在示例中讨论了查询交易,但是可以调用智能合约来更新被调用的链码的世界状态。请参阅以下注意事项。
6. 注意事项
- 通常,每个链码中都将包含一个智能合约。
- 如果多个智能合约关系密切,则仅应将它们部署在同一链码中。通常,仅当它们共享相同的世界状态时才需要这样做。
- 链码命名空间提供了不同世界状态之间的隔离。通常,将不相关的数据相互隔离是有意义的。请注意,你不能选择链码命名空间。它由 Hyperledger Fabric 分配,并直接映射到链码的名称。
- 对于使用
invokeChaincode()
API 的链码到链码交互,必须将两个链码都安装在同一对端节点上。- 对于仅需要查询被调用链码的世界状态的交互,调用可以在与调用者链码不同的通道中进行。
- 对于需要更新被调用链码的世界状态的交互,调用必须与调用者链码在同一通道中。
Reference
- Docs » Developing Applications » Application design elements » Chaincode namespace, https://hyperledger-fabric.readthedocs.io/en/release-1.4/developapps/chaincodenamespace.html
- Docs » Key Concepts » Ledger, https://hyperledger-fabric.readthedocs.io/en/release-1.4/ledger/ledger.html#world-state-database-options
- https://fabric-shim.github.io/master/index.html
项目源代码
项目源代码会逐步上传到 Github,地址为 https://github.com/windstamp。
Contributor
- Windstamp, https://github.com/windstamp