Hyperledge 区块链开发教程(一)

原文:zh.annas-archive.org/md5/7f932e9670331dae388d1a76f72881d8

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

这个学习路径是你探索和构建使用以太坊、Hyperledger Fabric 和 Hyperledger Composer 的区块链网络的简易参考。它从区块链的概述开始,向您展示如何为开发、打包、构建和测试去中心化应用程序设置以太坊开发环境。您将学习 Solidity - 在以太坊中开发去中心化应用程序的事实上语言。您将配置 Hyperledger Fabric,并使用这些组件构建私有区块链网络和连接到它们的应用程序。从原则出发,您将学习设计和启动网络、在链代码中实现智能合约,以及更多。

在完成这个学习路径时,您将能够通过处理区块链生命周期中遇到的关键痛点来构建和部署自己的去中心化应用程序。

本书适合谁

这个学习路径是为了想要从零开始使用 Hyperledger 构建去中心化应用和智能合约的区块链开发者而设计的。对于任何编程语言的基本了解或接触都将有助于开始本课程。

本书涵盖内容

第一章区块链 - 企业和行业视角,你已经听说过区块链,想知道为什么会有这么多吵闹?在本章中,我们探讨了为什么区块链是一个颠覆性创新,它带来了什么创新,以及技术领域是什么。

第二章探索 Hyperledger Fabric,从理解区块链格局开始,然后我们将注意力转向 Hyperledger Fabric。本章的目标是在揭示/构建架构的同时,带领您逐步部署 Hyperledger Fabric 的每个组件。

第三章用业务场景做铺垫,描述了一个业务用例,然后专注于理解从需求到设计使用区块链创建良好业务网络的过程。

第四章使用 Golang 设计数据和交易模型,旨在定义 Hyperledger Fabric 中智能合约的组成部分。它还将向您介绍有关智能合约的一些术语,并让您体验使用 Go 语言开发链代码的过程。

第五章暴露网络资产和交易,利用上一章中编写的智能合约,本章关注应用程序与网络的必要集成。它通过配置频道、安装和调用链代码来带领读者从客户端应用程序考虑可能使用的各种集成模式。

第六章商业网络,旨在介绍和揭示建模商业网络所需的技能和工具。在更高层次的抽象水平上工作,基础、工具和框架将为读者提供一种快速建模、设计和部署完整端到端的商业网络的方法。

第七章一个商业网络示例,将前一章节的概念付诸实践,本章将步骤性地介绍如何部署一个完整的商业网络,从终端用户应用到智能合约。

第八章区块链网络中的灵活性,重点介绍了在区块链网络中保持灵活性所需的方面。应用 DevOps 概念,向读者呈现了一个持续集成 / 持续交付管道。

第九章区块链网络中的生活,旨在提高读者对采用分布式分类帐解决方案时组织和联盟可能面临的关键活动和挑战的认识,从应用程序变更的管理到维护足够的性能水平。一个成功的网络部署将有望看到许多组织加入其中,以及交易数量的增加。

第十章治理 - 受监管行业的必要之恶,治理对于受监管行业来说是必要之恶,但治理不仅适用于处理受监管行业用例的商业网络。这也是一种确保商业网络长期性和可扩展性的良好实践。本章探讨了任何创始人领导的区块链网络生产准备的重要考虑因素。

第十一章超级账本 Fabric 安全,为区块链网络的安全设计打下基础。讨论了各种安全构造,并详细解释了超级账本 Fabric 的安全性。这是理解安全性设计考虑的重要章节。

第十二章区块链技术简介,概述了关键概念,如密码学和哈希算法、分布式分类帐、交易、区块、工作证明、挖矿和共识。我们详细介绍了比特币,区块链技术的鼻祖。我们通过指出比特币的一些局限性以及以太坊是如何解决这些问题的来简要介绍以太坊。虽然比特币和以太坊是公共区块链的例子,但 IBM 的超级账本用作企业区块链的示例。在本章末尾,我们将审视区块链的演进,从 1.0、2.0、3.0 等等,以及它们的用例。

第十三章以太坊基础知识,涵盖了以太坊的基本概念,如智能合约、以太币、共识算法、以太虚拟机(EVM)、Gas 和账户等。我们将讨论以太坊的性能,并探讨如何通过工作证明、卡斯珀、Plasma 和分片等方式提高整体性能。

第十四章Solidity 编程概览,讨论了什么是 solidity,以及 solidity 开发环境的工具。然后我们讨论智能合约及其常见模式。我们涵盖了智能合约安全的重要主题。最后,我们展示了如何编写众筹用例的智能合约。

第十五章构建以太坊区块链应用程序,探讨了什么是 DApp。我们快速概述了 web3.js。我们解释了如何设置以太坊开发环境,以及如何开发和测试 DApp。

第十六章使用 Hyperledger Fabric 探索企业区块链应用程序,深入介绍了 Hyperledger Fabric 的关键概念,以及核心组件。我们解释了如何创建一个 Hyperledger Fabric 环境,如何编写链码,以及如何设置 Hyperledger Fabric 配置。

第十七章使用 Hyperledger Composer 实现业务网络,提供了对 Hyperledger Composer 的概述,讨论了如何设置 Hyperledger Composer 环境。我们讨论了业务场景、业务网络归档以及如何实现业务交易功能。

第十八章区块链使用案例,首先讨论了跨行业流行的区块链使用案例,包括金融领域、公共服务、供应链、物联网IoT)和医疗保健等。然后我们将继续讨论 DApps 的适当使用案例,然后开发一个成功的 DApp。最后,我们以健康数据共享的使用案例为例,就如何建立适合的 DApp 进行高层次评论。

充分利用本书

我们专注于组织和流程。内容确保不仅易于跟随和自然流动,而且具有主题模块化性。每一章探索了区块链的一个方面。虽然专门讨论了 Hyperledger 项目,但核心关注领域是适用于区块链技术学科的。

本学习路径旨在成为进入区块链技术世界的发展路径。这些章节的安排旨在确保可以轻松跟随并自然流畅地展开。

商业用户可以跳过详细描述如何开发区块链应用程序的章节,而是专注于对技术和使用案例的概述的章节。

建议 IT 用户下载代码并进行修改,以适应他们自己的用例或练习。

下载示例代码文件

你可以在 www.packt.com 的账户中下载本书的示例代码文件。如果你在其他地方购买了这本书,你可以访问 www.packt.com/support 并注册,文件将直接通过电子邮件发送给你。

您可以按以下步骤下载代码文件:

  1. 登录或注册 www.packt.com

  2. 选择“SUPPORT”选项卡。

  3. 点击“Code Downloads & Errata”。

  4. 在搜索框中输入书名,然后按照屏幕上的指示操作。

下载文件后,请确保使用最新版本的解压缩软件解压文件夹:

  • Windows 上使用 WinRAR/7-Zip

  • Mac 上使用 Zipeg/iZip/UnRarX

  • Linux 上使用 7-Zip/PeaZip

本书的代码捆绑包还托管在 GitHub 上,网址为 github.com/PacktPublishing/Blockchain-Development-with-Hyperledger。如果代码有更新,将在已存在的 GitHub 存储库中进行更新。

我们还有其他代码捆绑包来自我们丰富的图书和视频目录,可在 github.com/PacktPublishing/ 上查看!

使用规范

书中的代码词汇,数据库表名,文件夹名称,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 账号显示如下:“发单方属于自己的组织,被称之为 TradeOrdererOrg。”

一段代码块设置如下:

- &ExporterOrg
  Name: ExporterOrgMSP
  ID: ExporterOrgMSP
  MSPDir: crypto-config/peerOrganizations/exporterorg.trade.com/msp
  AnchorPeers:
    - Host: peer0.exporterorg.trade.com
    Port: 7051

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:

pragma solidity ⁰.4.15;
import 'zeppelin/contracts/math/SafeMath.sol';
….
contract ExampleCoin is ERC20 {
  //SafeMath symbol is from imported file SafeMath.sol'
  using SafeMath for uint256;
   …
}

任何命令行输入或输出,请按如下方式写成:

mkdir ~/insurance-claim && cd ~/insurance-claim

粗体:表示一个新术语,重要词汇,或在屏幕上看到的词汇。例如,菜单或对话框中的词汇会显示在文本中像这样。例如: “当前请求经过矿工节点验证后,将调用 HelloWorld 智能合约。”

警告或重要注释看起来像这样。

小贴士和技巧看起来像这样。

第一章:区块链 - 企业和行业视角

区块链承诺从根本上解决时间和信任问题,以解决金融服务、供应链、物流和医疗保健等行业的效率和成本问题。区块链的关键特征包括不可变性和共享账本,在这个账本上,交易更新由共识驱动的信任系统执行,这可以促进多方之间真正的数字交互。

这种数字交互不仅受制于系统信任,而且确保交易记录的来源保持不可变的互动记录。这个特点本身就促使了可追责性和不可否认性,并激励公平竞争。通过区块链系统设计,我们试图建立一个隐含信任的系统。这种信任系统导致了风险的降低,以及各种应用技术构建,如密码学、加密、智能合约和共识,本质上创造了门槛,不仅降低了风险,而且还为交易系统注入了额外的安全性。

在本章讨论中,我们将涵盖区块链的以下几个方面:

  • 定义区块链

  • 区块链解决方案的构建模块

  • 安全交易处理协议的基础知识

  • 区块链的应用

  • 企业中的区块链

  • 企业设计原则

  • 选择区块链框架的业务考虑

  • 选择区块链框架的考虑因素

定义术语 - 什么是区块链?

在技术层面上,区块链可以被定义为一个不可变的账本,用于记录交易,由一个互不信任的对等分布式网络维护。每个对等方都保留着账本的副本。对等方执行共识协议来验证交易,将其分组成块,并在块之上构建哈希链。这个过程通过将交易排序为保持一致性所必需的方式来形成账本。区块链随着比特币( bitcoin.org/)的出现而崭露头角,并被广泛认为是在数字世界中运行受信任交易的一项有前途的技术。

支持加密货币的区块链在公开或无许可方面,即任何人都可以参与而无需特定身份。这类区块链通常使用基于工作证明PoW)和经济激励的共识协议。相比之下,许可区块链作为在一群已知、可识别参与者之间运行区块链的替代方式而发展。许可区块链提供了一种确保共享相同目标但不完全信任对方的实体之间相互作用的方式,例如互相交换资金、商品或信息的企业。许可区块链依赖于其对等方的身份,并通过传统的拜占庭容错BFT)共识来实现。BFT 是一种协议,已广泛用于 IT 解决方案中,用于就网络失效节点状态达成一致。该协议基于拜占庭将军问题,即一群将军需要就他们的战略达成一致,但其中一人可能是叛徒。

区块链可以执行智能合约形式的任意可编程交易逻辑,正如以太坊(ethereum.org/)所示。比特币中的脚本是这一概念的前身。智能合约充当可信的分布式应用程序,在其中安全性来自区块链和同行之间的共识。

对于企业希望利用区块链平台的企图,区分许可和无许可区块链至关重要。用例决定了技术的选择,技术取决于共识系统、治理模型、数据结构等。通过使用许可区块链,我们可以以更好的方式做我们已经在做的一些事情,这可能具有重要意义。在接下来的图表中,您可以看到一个银行联盟如何使用超级账本(Hyperledger),这是一种许可区块链类型,用于清算和结算而无需依赖中央清算机构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清算机构的创建是因为银行彼此之间并不完全信任,在交易之间充当中介降低了一方不履行其义务的风险,这导致了一场关于许可与无许可区块链的漫无止境的讨论,而本章不会涉及这场辩论,区块链可以提供一种改变或颠覆当前业务和商业模式的方式。在受监管行业中,大多数用例采用许可区块链模型。

这是由于监管要求和交易处理的经济可行性,而无论选择的区块链模型如何,区块链都提供了许多转型和颠覆的可能性。

区块链作为一种技术平台具有非凡的潜力。在企业中,区块链可以提供:

  • 一种设计方法,将交易数据、价值和状态固有地靠近业务逻辑

  • 通过社区验证的安全业务交易执行,在安全流程中促进信任和坚固的交易处理,这是区块链基础的。

  • 符合现有法规的替代、权限化技术

区块链承诺解决长期存在的行业问题——这正是它的潜力所在,解决类似于现代化金融和贸易系统的问题,加速证券和贸易结算。

区块链框架的四个核心构建模块

区块链框架通常包括以下四个构建模块:

  • 共享账本:共享账本仅附加分布式交易记录。比特币区块链的设计初衷是民主化可见性;然而,使用区块链时,也需要考虑消费者数据法规。使用配置正确的 SQL 或 noSQL 分布式数据库可以实现不可变性或仅附加的语义。

  • 密码学:区块链中的密码学确保身份验证和可验证的交易。区块链设计包括这一重要因素,因为关注于假定计算难度和使加密对对手更难破解。这是比特币区块链的一个有趣挑战,因为涉及经济激励和其系统设计。在一个不那么民主或权限的商业账本网络中工作时,对密码学的考虑会改变。

  • 信任系统或共识系统:信任系统是指利用网络的力量来验证交易。

    在我看来,信任系统是区块链系统的核心;它们是区块链应用的核心,并且我们相信“信任系统”是首选术语,而不是共识系统,因为不是所有的验证都是通过共识完成的。这种信任系统的基础元素决定了对区块链基础设施的整体设计和投资。随着区块链领域的每一个新参与者,信任系统都在修改,形成了为特定的区块链用例专门化的变体。信任、交易和所有权是区块链技术的基本要素。对于公司间交易,信任系统管理着参与公司之间的交易。

    仍然需要做很多工作来定义特定用例的最佳信任系统,比如 P2P 和共享经济模式与 B2B 模式。

  • 业务规则或智能合约: 智能合约是内嵌在区块链交易数据库中并通过交易执行的业务条款。这也是区块链解决方案的规则组成部分。需要定义每个交易的价值流和状态。

以下的使用图表很好地解释了这些概念:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这四个基本模块是被广泛接受和理解的。它们在区块链出现数十年前就存在了。共享账本是一种进化性的变化,类似于从纸张表格过渡到基于计算机的电子表格,但根本的业务规则保持不变。

其他需要考虑的能力

企业区块链提案中还应包括什么?以下是需要考虑的其他能力的非穷尽列表:

  • 审计和日志记录: 在区块链解决方案中包含审计和日志记录可以帮助解决法规,用于不可否认、技术根本原因分析、欺诈分析和其他企业需求。

  • 企业集成: 还值得考虑解决方案将如何集成到企业中:

    • 与现有记录系统的集成(*SoR)的目标是确保区块链解决方案支持您现有的系统,如 CRM、商业智能、报告和分析等等

    • 作为交易处理系统的集成: 如果您想保留记录系统作为采纳区块链的临时方法,将其集成为交易处理系统是有意义的

    • 具有包含区块链的意图的设计: 对现有系统产生最小影响的路径将加速企业对区块链的采用。

  • 监控: 监控对于解决法规问题和确保高可用性、容量规划、模式识别和故障识别非常重要。

  • 报告和监管要求: 准备好解决监管问题对于区块链作为交易处理系统的临时采用也非常重要。建议您与现有的 SoR 对接,以卸载报告和监管要求,直到区块链变得企业感知,或者企业软件对区块链感知。

  • **企业身份验证、授权和会计 **要求: 在受许可的企业世界中(不同于无需许可的比特币区块链),所有区块链网络参与者应被识别和跟踪。如果他们要在生态系统中扮演角色,他们的角色需要被定义。

安全事务处理协议的基本原理

我们之前提到,密码学是区块链解决方案的核心构建模块之一。比特币区块链的基本安全性在于账本的所有主要组件之间的优雅的密码连接。具体来说,交易之间通过默克尔树主要相互连接。默克尔树基于树数据结构的概念,其中每个叶节点都有其数据的哈希计算,而非叶节点具有其所有子节点的哈希。这种方法不仅提供了确保数据完整性的方式,而且通过允许删除被视为私有的叶子但保留哈希来提供隐私特性,从而保持了树的完整性。默克尔树的根已经合并到块头中。块头包括对其之前的块头的引用。

密码学强制的相互连接促进了分布式分类账的稳定性和安全性。在任何时候,如果任何组件之间的连接断开,都会使它们容易受到恶意攻击:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

交易也通过默克尔树与区块链结构的其余部分进行了密码学连接。一旦在一个块中修改了交易,并且其他所有部分保持稳定,那么该块的所有交易与其头之间的链接就会断开:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

新生成的默克尔树根与已经在块头中的根不匹配,因此不能连接到区块链的其余部分。如果我们继续更改块头中的默克尔树根,那么我们将破坏块头链,从而破坏区块链本身的安全模型。因此,如果我们只更改一个块的内容,则区块链的其余部分保持稳定和安全,特别是由于块头通过在下一个块的头部包含上一个块头的哈希来提供连接的链接。

区块链技术的发展历程及未来走向

区块链已经是商业颠覆者,我预计它将在不久的将来显着改变行业、政府和我们的生活。

伟大的分水岭

加密货币和首次代币发行ICO)世界之间存在显着的分歧,以及受监管业务的世界。后者包括银行和金融机构共同努力评估市场潜力和运营效率。

这一分歧的双方都利用了区块链周围的动能来推进各自的利益。区块链生态系统挑战了现状,并且不顾一切地表明了一个观点——通常行为像一个青少年。它受到新商业模式、去中心化的承诺和有趣的技术创新的推动。随着区块链势头的增长,比特币和其他加密资产的价值正在飙升,现在 ICO 已经出现,它打破了传统的筹款监管框架。

在企业方面,有越来越多的行业倡议围绕清算和结算,以实现更快的结算和银行间转账、透明度通过数字化、供应链中信息的对称传播以及在物联网IoT)设备之间创建临时信任。

这里有一个共同的主题——区块链将会留下来。随着它不断发展,并为行业使用案例生成创新解决方案,它将不断迈向成熟,并在信任基础上兑现其效率和显著成本节约的承诺。

区块链交付的经济模型

由区块链技术支持的商业网络可能会给行业带来转型或颠覆,但无论如何,为了蓬勃发展,区块链都需要一个经济模型。如果颠覆是目标,那么技术、人才和市场协同的投资可以与经济激励的诱惑相结合。例如,ICO 通常依赖于 Tokenomics,这是描述这些网络中价值生成经济系统的术语。代币是系统或网络通过为提供者或消费者创造平台,或通过共同创建一个各方都可以利用的自我管理的价值网络来生成的价值单位,以便创建、分发和共享符合所有利益相关者利益的奖励。

大部分由加密货币资助的 ICO 前沿打破了风险资本(由众筹项目领导)当前的筹款机制,而且,区分证券和实用币的区别是原则上具有颠覆性的。

ICO 正在寻求创建一个建立在去中心化开放治理(或自我治理)和透明度原则上的经济体系,一个奖励创新并消除去中心化的系统。ICO 看到了一些初期的失败和一些成功,但它们仍然提供了未来的预览,在那里加密资产将成为一个基本的价值单位——其估值和可替代性由它们来源的网络定义——推动着一个建立在和围绕创新的经济体。

在企业方面,更加关注理解技术并重新构想生态系统、商业网络、法规、保密性和隐私以及影响各行业区块链网络的商业模式。希望探索区块链的企业想要看到快速的验证点,能够迅速展示结果并帮助他们与区块链进行创新的用例。

区块链通过提供内置的交易数据控制、出处和历史背景,帮助各行业实现信息更对称地传播。这可以带来更高效的工作流程和转变的业务流程。然而,许多早期项目并没有关注区块链的核心原则,导致了中介去除、去中心化和强大的自我治理模式。不过,这背后也有一个很好的理由:行业和传统企业往往专注于当前的商业议程、模式、增长以及最重要的,法规合规和遵从。对当前业务运营的强调意味着它们并不自然地倾向于颠覆性模式。

边学边做

对于任何新技术,都存在一个学习曲线。随着区块链的发展,我们开始与受监管的行业合作,我们很快意识到在这些行业中,有一些重要的设计考虑需要解决,比如保密性、隐私、可扩展性和性能等。当涉及到设计区块链网络以及管理这些网络的商业模式时,这些元素可能会产生重大的成本影响。这些挑战不仅很有趣,而且对传统的受监管行业和企业产生了积极的影响,通过重新激发这些组织中的创新,并邀请最优秀的人才加入解决这些挑战。企业正在看到,由区块链技术驱动的生态系统和网络将有助于进步和成功。

需要开始揭示一种激励模式,以激励组织加入一个促进奖励的平台的想法,从而使所有利益相关者受益。 tokenomics 背后的经济激励不能被很多传统企业和行业盲目采纳,但这并不意味着这些行业不应该开始探索可能的商业模式,以实现价值创造,并推动一些急需的现代化努力。

信任和问责制的承诺

区块链技术承诺成为一个安全交易网络的基础,可以在许多饱受信任和问责制系统性问题困扰的行业中诱发信任和安全。从技术角度来看,区块链促进了一个安全、透明、可审计、高效和不可变的交易处理和记录系统。这些技术特征适用于解决当前分布式交易系统所困扰的时间和信任问题。

区块链从根本上改变了多层模型,转向了扁平层的交易处理模型。这有望通过去中介化,通过在新的系统设计中引入高效性,或简单地创造新的商业模式来从根本上削弱行业。

去中介化表示减少生产者和消费者之间的中介使用,比如直接在证券市场投资而不是通过银行进行交易。在金融行业,每笔交易历来都需要有一方来处理交易。去中介化包括移除中间商,从定义上来说这会破坏基于中介的商业模式和激励经济体。近年来,由数字技术带来的波澜涌起,这正是由市场洞察和组织提供更丰富的用户体验的欲望所推动。

区块链是一项旨在通过引入交易、信任和所有权的技术来推动这一变革。区块链数据库和记录所代表的技术模式具有潜力从根本上改善银行业、供应链和其他交易网络,为创新和增长提供新机会,同时降低成本和风险。

把区块链技术应用到工作中的行业

让我们简单看一下区块链的应用场景:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

企业中的区块链

现在我们已经看到区块链在各行业中的发展,让我们谈谈企业应该如何使用区块链的原则。企业为什么要将区块链技术应用于其系统或应用程序之一?

哪些应用适合?

组织需要在应用设计过程中建立使用标准以帮助他们评估应该在哪些方面最好地应用区块链技术。以下是一些标准的例子,可以帮助企业确定哪些应用或系统将受益于此:

  • 遵循交易、信任和所有权原则的应用:如先前所述,这三个原则——交易、信任和所有权对于任何区块链系统都是至关重要的。交易和所有权意味着分类账条目的变更和转移,而信任指向交易系统的无需信任的性质。

  • 基本上是交易性质的应用:关于为什么我们不能从分布式数据库,即非 SQL 或关系型数据库中获得区块链的好处,经常会有争论。但是多方交易是使应用适合区块链的关键。这需要有长时间运行的进程,有大量微型交易将由区块链支持的交易系统验证和验证。然而,数据库仍然可以用于持久性或复制以适应企业系统。其他考虑因素包括可能会随时间增加的小数据集大小、日志开销等。

  • 由非垄断参与者组成的商业网络:这第三个标准涉及分布式与去中心化计算模型。区块链信任系统可以在任何模型中工作;然而,区块链商业网络的信任方面来自具有非垄断参与(联合许可网络模型)的多方参与者。垄断参与可能是可以接受的(私有许可网络模型),但是必须制定一种信任模型,确保即使参与者具有理性行为,也能防止中心化控制。许多内部用例不遵循这一原则,更多地用于分布式应用程序模型。

对于试图理解或确定在哪里有意义地应用区块链的企业,有一种简单的方法可以思考用例选择。一个适当的用例对于可持续的区块链解决方案将实现长期业务目标,并提供强大的技术投资回报。

这始于一个企业问题——一个足够大,以至于企业要花费资源/时间的问题——以及认识到具有相同问题的同伴。当公司意识到企业问题也是行业问题(如安全借贷、抵押借贷等)时,他们找到了区块链潜力最大的用例。

当组织正在确定区块链的各个方面对其企业应用的益处时,他们还需要认识到整个区块链领域的碎片化。有许多创新方法可用于解决特定挑战与区块链。许多供应商提供专门用于解决特定用例的信任系统的变体,并且他们已经定义了在给定行业中区块链将最受益的用例,例如。这些专业供应商通常承诺提供快速解决方案,以满足消费者对快速数字交互的需求。

区块链的原则在传递快速的消费者驱动的结果方面非常有帮助,比如分散的、分布式的、全球的、永久的、基于代码的可编程资产,以及交易记录。我们在考虑将区块链视为解决每个企业应用的问题的工具时要小心,但它可以在许多交易应用中起到作用。

现在,让我们讨论企业对区块链的看法,以及企业采用这项技术所面临的一些挑战。在接下来的部分中,我将重点关注三个领域,这些领域有助于在企业背景下确定区块链的基调。

企业如何看待区块链?

激进的开放性是区块链作为数字信任网络的一个方面,但在企业中,考虑激进的开放性的影响和意义是至关重要的。

公共区块链可以以极其简单的方式运作,支持所有交易的高度分布式的主列表,通过匿名共识支持的信任系统进行验证。但企业能直接应用无信任系统的模式而不修改区块链的基本原则吗?

组织将这种颠覆性技术视为他们变革的一条道路,还是仅仅是帮助他们改进现有流程以利用信任系统所承诺的效率的工具?无论怎样,企业都希望对区块链的采用对现有系统的干扰尽可能小,并且这并不容易实现!毕竟,现有系统的设计缺陷正是促使企业考虑这种范式转变的原因。很多关于区块链的概念和用例距离企业的实际应用还有很远的路要走。

第一个尝试和采用区块链的行业是金融服务领域,因为他们一直面临着被另一波创业公司颠覆的恐惧。像许多行业一样,它也受到了消费者对更快速、低成本交易的需求的驱动。金融服务有一系列明确定义的用例,包括贸易融资、贸易平台、支付和汇款、智能合同、众筹、数据管理和分析、市场借贷以及区块链技术基础设施。我们在这个行业看到的对区块链的应用可能会渗透到未来的其他行业,比如医疗保健、零售和政府。

区块链是一种新兴技术,汇集了许多好的想法,但对于企业的使用仍需进一步发展。在多领域链之间促进互操作性的缺乏明确定义的标准可能是一个挑战。因此采用它的企业将需要建立能力,以便他们可以为进一步的创新作出贡献,并帮助必要的区块链标准开发。这反过来可能有助于为改进现有业务实践和在基于区块链的信任网络中开发新业务模式提供独特的机会:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用于证明区块链技术应用的滤纸试验

从根本上讲,区块链解决了交易经济的三个方面:

  • 交易

  • 拥有权

  • 信任

区块链的显著技术元素包括:

  • 信任系统背后的技术:共识、挖掘和公开账簿

  • 开放网络上的隐秘通信:密码学和加密

  • 不可否认的系统:可见性到一堆过程

虽然区块链技术的影响可能很深远,但组织应制定一套特定于企业的标准,可应用于可能偏向企业区块链的现有或新项目。

鉴于区块链技术的多功能性和当前的炒作曲线,企业应该使用链决策矩阵作为工具,以确保他们对待业务领域应用基础技术的方法是有结构的。这种方法也将有助于一个一致的区块链基础设施和信任系统管理,这在许多应用驱动链发展并且对企业的可见性、管理和控制需求增长的情况下将非常重要。

为整个企业集成区块链基础设施

任何企业采用区块链技术都应该以颠覆现有系统为目标。考虑与企业的记录系统集成是朝这个方向努力的一种方式。通过这种方式,企业可以实现基于区块链的交易处理,并将其现有的记录系统用作其其他应用程序(如业务智能、数据分析、监管互动和报告)的接口。

将企业区块链技术的基础设施与利用链技术获得竞争优势的业务领域分开是至关重要的。区块链可以被视为一种对企业不可见的企业链基础设施,它在幕后运作,同时促进各种业务驱动链之间的企业协同。这个想法是将业务领域与支持它的技术分开。链应用应由具有合适信任系统的业务领域配置。正如我一再强调的那样,信任系统对任何区块链努力都至关重要,因此它应该符合特定业务应用的需求。基础设施和计算需求的成本将由企业可用的信任系统的选择决定。

通过将区块链技术基础设施分离出来,围绕可插拔的信任系统设计架构,利用信任中介和促进灵活性的设计以及模块化信任系统,企业可以专注于业务和监管要求,如 AML、KYC、不可否认等。区块链应用的技术基础设施应该是开放的、模块化的,并且适用于任何区块链变种,从而使区块链努力易于管理。

企业协同表明要推动多个企业区块链之间的协同,以实现企业内部和企业间链(跨链)连接。在这种模式下,交易将穿过各种信任系统,使企业治理和控制系统能够看到交互行为。在审视业务部门和外部企业之间的这些交互作用时,应考虑分形可见性及其关联的企业数据保护。一个无形的企业链基础设施可以为发展企业连接器和公开 API 提供坚实的基础,使现有系统更具链感知能力。

由于业务链之间的有条件可编程合约(智能合约),企业协同将得到促进:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

企业如何知道自己是否已准备好采用区块链?更重要的是,在考虑区块链消费时,它应该将重点放在与现有交易系统的整合上,还是应该考虑企业感知的区块链基础设施?

要充分利用企业区块链的承诺,一个整合型企业将需要不止一个用例,并且需要推动企业协同。最成功的区块链消费策略应该首先关注技术,然后考虑与现有企业业务系统的整合。这将促进集体理解,并加速企业采用区块链的过程,希望能够选择最少干扰的路径。

企业设计原则

正如先前所述,区块链技术承诺成为一个安全交易网络的基础,在受到围绕信任和问责制的系统性问题困扰的行业中引发信任和安全感。它旨在产生市场和成本效益。

在过去的几年里,随着区块链技术的成熟,我们关注的重点是企业和商业如何利用这项技术来解决痛点并开启新的商业模式。已经开始看到区块链潜力的组织现在开始重塑受陈旧流程、文书工作和技术成本困扰的业务网络。

业务驱动和演进

在最近的过去,组织会将内部业务系统和 IT 基础设施延伸到互联网上,以利用互联和可访问系统的协作潜力。区块链技术正在将这一趋势推向新的高度,提供由可信赖的业务网络促成的真正数字化互动。在互联网时代,成功的企业采用并适应了技术挑战,而在区块链时代,业务而不是技术成为了推动力。

虽然区块链技术本身很有趣,但业务网络还涉及许多其他机制,这些机制也应该进行评估,包括:

  • 共识模型:哪种信任体系最适合您的业务网络?

  • 控制和治理:允许哪些实体做什么?如果系统出现异常,调查过程将由谁来负责?

  • 数字资产生成:谁在系统中创建资产?谁来管理它?

  • 发行权力:在一个真正分散的系统中,权威的概念不再具有一致性。那么在区块链网络中,谁将负责治理、追究责任,以及最终的监管呢?

  • 安全考虑:网络将如何解决企业安全问题,包括共享业务网络带来的新安全挑战?

我们设想一个专为多个业务领域而设的区块链网络,例如,抵押贷款、支付、交易、特定资产类型的清算和结算等。在企业环境中,我们设想一个中心化的网络,其中志同道合的业务实体共享一个共识联盟。支持这一中心化网络概念的几个实际理由包括以下几点:

  • 使用特定领域的业务语言,导致智能合约的构建、管理和治理,作为代理业务表示的一部分

  • 定义了一种资产类型,导致数字资产的治理、管理和估值(用于交易、可替代性等)

  • 适当的监管,考虑到每个行业和业务网络都有单独的监管,因此遵守监管和其他相关成本的负担可以在业务网络中共享。

  • 其他相关业务功能,如分析、分析、市场数据等

我们已经介绍了企业区块链的商业动力,接下来让我们考虑如何确保区块链网络的可持续性和长期性。

确保可持续性

基于区块链的业务网络正在不断发展壮大,随着它们的发展,核心问题如信任模型、数据可见性和利用网络获取竞争优势等问题将不可逆转。

关注可持续性似乎是矛盾的,因为它促进了开放的协作创新,同时又锁定了诸如共识或信任系统以及用于管理资产、智能合约和多方交易网络中的整体交互的治理系统等结构。区块链系统设计需要考虑所有这些因素。

成功的系统设计需要与多方场景中的区块链原则(包括贸易、信任、所有权和交易性)相匹配。如果不基于这些核心原则构建业务网络,可能无法以可持续的方式实现区块链技术的承诺。

以下是支持和维持区块链业务网络增长的七项设计原则:

  • 网络参与者需要控制自己的业务

  • 网络必须是可扩展的,以便参与者可以灵活地加入或离开网络

  • 网络必须是经过许可的,但也必须受保护,以保护竞争数据,同时促进点对点交易

  • 网络应允许开放访问和全球协作进行共享创新

  • 网络必须可扩展,既可用于事务处理又可用于加密数据处理

  • 网络必须能够容纳企业安全,并应对新的安全挑战

  • 网络需要与企业中已建立的记录系统和交易系统共存

我们将以图形方式列出设计原则,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

推动区块链采用的原则

在任何企业中,区块链采用都是由三个原则驱动的:业务蓝图、技术蓝图和企业集成。

选择区块链框架时,根据以下三个原则考虑一些不可或缺的事项:

  • 业务蓝图:区块链承诺创建一个基于信任的价值业务网络。为了做到这一点,理解各种区块链框架如何处理网络交互模式、低效和漏洞至关重要。

  • 技术蓝图: 如果技术要与业务目标保持一致,组织就需要为其需求做出适当的技术和架构选择。在这里可能会考虑每秒交易数(TPS)、企业集成、外部系统集成以及监管和合规性要求。这些决策都是适当预算区块链采用所必需的技术尽职调查的一部分。

  • 企业集成: 将区块链集成到企业系统中,特别是邻接系统,是一个重要的商业和技术考虑因素(因为下游交易系统影响关键业务系统),以及一个成本因素。根据我的经验,如果组织在规划的早期不专注于邻接系统的集成,可能会阻碍采用,因为它对区块链项目的成本影响显著。

在接下来的章节中,我会稍微详细地讨论每个设计考虑因素。

选择区块链框架的业务考虑因素

当组织在评估是否采用区块链来解决他们的痛点时,会涉及到许多标准。以下是一些从业务角度考虑的因素:

  • 开放平台和开放治理: 企业选择的技术标准将为企业区块链采用、合规性、治理以及解决方案的总体成本奠定基础。

  • 解决方案的经济可行性: 无论组织选择哪种区块链框架,都应该提供与其现有业务模型、退款、计算权益和帐户管理的成本对齐。这与投资回报率息息相关。

  • 解决方案的长期性: 当组织努力构建一个可信的网络时,他们希望确保他们能够承担网络的成本和运营,以便它能够增长和扩展以容纳更多的参与者和交易。

  • 监管合规性: 合规性问题与交易处理密切相关,可能包括行业特定的报告和分析事件,用于业务工作流和任务,无论是自动化的还是以人为中心的。

  • 与邻接系统的共存: 区块链网络需要能够与企业的其余部分、网络参与者和邻接系统共存,这些系统可能具有重叠和互补功能。

  • 业务增长的可预测成本: 业务增长依赖于可预测的指标。历史上,许多行业都关注每秒交易数,但是这种测量因系统设计、计算成本和业务流程而异。

  • 技能和人才的获取: 人才的可获得性影响着成本以及随着行业和技术的不断创新,维护和区块链解决方案的长期性。

  • 技术供应商的财务可行性:在选择供应商时,重要的是考虑他们在长期支持和您的区块链解决方案的长期使用方面的可行性。您应该审查供应商或业务伙伴的长期愿景和可持续性的商业模式。

  • 全球范围的支持和支持:区块链解决方案往往涉及具有全球影响力的商业网络,以及支持网络扩张而又最小化中断的相关技能。

  • 依赖技术和行业特定标准:标准至关重要,不仅有助于标准化共享技术堆栈和部署,而且有助于为行业专家建立有效的沟通平台,用于解决问题。标准使低成本、易消费的技术成为可能。

区块链供应商提供各种专业化服务,包括:

  • 变体信任系统:共识、挖掘、工作证明等。

  • 锁定到单一信任系统

  • 专门为特定用例构建的基础设施组件

  • 通过概念验证的经过实地测试的设计

供应商不遵循基于标准化技术集的参考架构的技术风险是企业的碎片化区块链模型。

从业务角度来看,基于开放标准的区块链方法提供了灵活性,以及可插拔和模块化的信任系统,因此是最理想的选择。这种方法使企业能够接受专门的区块链,如 Ripple,为信任系统提供一个配置层,并提供一个具备支持其技术的独立业务领域。

选择区块链框架的技术考虑

当组织考虑区块链的技术影响时,他们应该从这样一个前提出发,即它不仅仅是另一个应用程序。它是一个生产网络,涉及风险和成本以确保正确的维护和维护。

在评估区块链技术影响时,以下是一些需要考虑的重要事项。

身份管理

身份管理是一个复杂而涉及的主题,特别是在受监管的行业,其中身份必须被管理并具有重大的业务后果,例如围绕了解客户(KYC)、反洗钱(AML)和其他报告和分析功能的活动:

  • 权限控制 是**成员注册证书(eCerts)和每个成员的交易证书(tCerts)**的概念;这些使实体能够获得权限和识别,同时完成交易。

  • 终端用户身份,由参与区块链网络的实体维护,是 LDAP/用户注册表到 tCerts 或交易 ID 的映射,以进行追踪(了解客户,以及了解客户的客户)

其他身份管理考虑因素包括:

  • LDAP 或现有的用户注册表不会消失,必须考虑为设计重点,因为成熟的身份验证和授权系统通常已经投入了大量投资和安全策略。

  • 信任系统是区块链技术的核心,并且必须为需要交易可追溯性的用例打开信任之路。

  • 区块链的身份和用于区块链的身份

  • 身份获取、审查和生命周期

  • 与基于用例的信任系统对齐

可扩展性

要考虑可扩展性,因为下游交易系统可能会影响关键的业务系统,这既是商业考虑,也是技术考虑。比如针对可扩展性的技术选择,例如用于共享账本的数据库选择、相邻系统集成、加密和共识,都会导致可以适应网络成员或交易增长的可预测成本的系统设计。

企业安全

有三个层面的企业安全需要考虑:

  • 物理 IT 基础设施层,其中包括特定用例的问题,如 EAL5、网络和基础设施隔离要求。

  • 区块链中间件层,其中包括加密模块要求、加密级别、数据存储、传输和数据静态加密、以及网络参与者之间数据的可见性。

  • 区块链共识(信任系统层),是区块链的核心,并且是保证基本数据存储特性的必要条件。如果网络中有更多参与者,他们必须带来资本以实现规模化。这就是关于在较低准入门槛下建立符合企业数据质量的共享数据存储的问题。共识,即使是最小的共识,在现有的架构中也是必要的。加密货币基础的信任系统和非加密货币基础的信任系统现在存在分歧。前者的模型,如 POW/PoS,不适合企业用例,希望创建权限区块链。

开发工具

开发工具选择包括集成开发环境,业务建模和模型驱动开发。

加密经济模型

加密经济模型指的是使用公钥密码学进行身份验证和经济激励来保证系统不会倒退或发生其他修改的去中心化系统。要充分理解区块链的概念和计算机科学中加密的好处,我们必须首先理解去中心化共识的概念,因为这是基于加密的计算革命的关键。

有系统性治理的去中心化

旧的范式是中心化的共识,其中一个中央数据库将决定交易的有效性。分散化方案打破了这一格局,将权威和信任转移给了分散化网络,并使其节点能够持续和顺序记录交易在公共块上,创建唯一的链—因此得名区块链。通过哈希码的密码学保证了交易来源的身份验证,消除了中央中介的需要。通过结合密码学和区块链,该系统确保没有重复记录相同的交易。

区块链系统设计应保留去中心化数字交易处理的理念,将其调整为一个许可网络,同时根据企业环境的需要集中一些监管合规和维护活动。

企业支持

企业支持区块链的重要性与重新考虑估算工作的原因相同。请记住,区块链不应被视为另一个应用程序。它是一个涉及风险和维护成本的生产网络,并且不能简单地使用现有的开发、基础设施和服务。

基于用例驱动的可插拔选择

为了确保您的区块链解决方案能够允许基于用例的可插拔选择,请考虑以下问题。

共享账本技术

您尝试通过区块链解决的用例、设计要求和问题都将有助于确定共享账本和数据库技术的选择。

共识

共识指导了信任系统并推动了区块链应用基础设施的技术投资,因此它是区块链的核心。此外,并不存在适用于所有用例的共识类型。用例定义了参与者之间的交互,并通过共识模型建议了最适合的信任系统。

共识是验证区块链网络上的网络请求或交易(部署和调用)顺序的一种方式。正确排序网络交易至关重要,因为许多交易依赖于一个或多个先前交易(例如,账户借方经常依赖于先前的贷方)。

在区块链网络中,没有单一的权威确定交易顺序;相反,每个区块链节点(或对等方)都有平等的发言权,通过实施网络共识协议来建立顺序。因此,共识确保了节点的多数同意了交易附加到共享账本的顺序。共识通过解决提出的交易顺序中的不一致来确保所有网络节点都在同一区块链上运行。换句话说,它确保了区块链网络中交易的完整性和一致性。

加密算法和加密技术

选择区块链系统设计可能受加密库和加密技术的指导。 组织的用例需求将决定这种选择,并推动区块链应用基础设施的技术投资:

  • 非对称: RSA(1024-8192),DSA(1024-3072),Diffie-Hellman,KCDSA,椭圆曲线密码学(ECDSA,ECDH,ECIES)与命名、用户定义和 brainpool 曲线

  • 对称: AES,RC2,RC4,RC5,CAST,DES,三重 DES,ARIA,SEED

  • 哈希/消息摘要/HMAC: SHA-1,SHA-2(224-512),SSL3-MD5-MAC,SSL3-SHA-1-MAC,SM3

  • 随机数生成:FIPS 140-2 批准的 DRBG(SP 800-90 CTR 模式)

用例驱动的可插拔选择

正如先前所述,用例将定义参与者之间的互动,并建议使用共识模型选择最适合的信任系统。

企业集成和设计可扩展性

设计区块链网络与组织中现有的记录系统并存是一个重要的成本考虑。 集成应通过业务和技术问题,因为下游交易系统会影响重要的业务系统。 通过与许多企业合作,我发现将区块链与相邻系统集成对其区块链项目的成本影响很大。 它确实需要在规划阶段早期解决,以免对企业采用产生不利影响。

想到运营问题也很重要。 通过保护贸易、信任和所有权的要素以及区块链的固有属性(如不可变性、出处和共识),信任系统承诺有助于消除冗余和重复的系统和流程。 这些重复成本组织大量资源,导致交易处理较慢和相关的机会成本。 区块链采用的一个目标应该是解决现有流程的核心痛点。 期望是一个透明的分类账,增加信任,节省时间和重大成本,并提供更好的客户服务。

对于网络可扩展性,设计可扩展性意味着在规划实施时考虑未来的增长。 扩展性衡量了系统的扩展能力以及实施扩展所需的工作量。 可扩展性在区块链业务网络设计中非常重要,不仅要适应业务的动态特性(包括所有的规定、竞争压力和市场动态),还要适应网络的增长(监管机构、市场制造商、扰乱、服务提供商等的增加)。

以下是一些设计考虑,以确保网络的可扩展性:

  • 成员灵活性:区块链网络可能从一个有限的参与者和角色群体开始,但后来可能会有新的参与者想要加入网络,而其他人可能想要离开。因此,您必须考虑成员变更的机制,包括对(共享)数据的访问。在设计可扩展性时,成员类型也是一个重要的考虑因素,因为成员的角色和类型可能随时间而变化。

  • 计算权益:基于加密货币的信任系统和基于计算权益的信任系统之间存在分歧,因此这是一个相当新的概念。参与者类型及其在网络中的业务利益类型是长期可持续的基础设施成本和维护的决定因素。例如,监管机构的成本模型可能与区块链驱动的业务网络的主要受益者的成本模型大不相同。

  • 共同的业务利益:区块链网络为企业提供了特定的优势,如降低风险、可靠且可预测的交易网络、较低的合规成本等。但这些共同利益可能导致其他运营问题,例如在实体加入和离开网络时的数据共享和所有权。由于围绕数据所有权的法规不断发展,以及行业对数据持久性的要求,因此在设计区块链系统时应仔细评估这些问题。

  • 治理:治理包括管理技术工件,如技术基础设施,并管理区块链网络中的数据和智能合约。建议在以下类别中分层治理:

    • 区块链网络/技术治理

    • 区块链数据治理

    • 区块链智能合约治理

    • 区块链交易管理治理

在设计可扩展性时,目标应该是确保区块链网络具有可持续的运营要素和业务增长要素。例如,在可持续模型中,每个参与者都可以部署管理其自身业务流程的链码,同时还可以控制变更业务流程、政策和监管要求。

其他考虑因素

除了上述提到的方面外,还有一些其他注意事项需要牢记。它们在以下部分简要解释。

共识、ACID 属性和 CAP

一致性模型永远不会降为 0,因为当 NoSQL 成为标准时,各种 NoSQL 系统通过理解 CAP 定理解决了它们的问题,而 RDBMS 企业社区则坚持了他们的 ACID 属性。区块链很可能提供了打破 CAP 并保持 ACID 的基本元素。以下是一些想法。

CAP

Cap 代表:

  • C—一致性:共识确保了发生的事情以及发生的顺序只有一个真相。

  • A—可用性:区块链的所有调用都是异步的,这意味着调用应用程序在确保一致性和持久性的同时可以取得进展(链接也保证了这一点)

  • P—网络分区:再次,共识防止了在网络分区后合并时出现冲突的分裂大脑

ACID

ACID 代表:

  • A—原子性:链码编程模型具有全有或全无的行为,允许你将活动分组在一起。要么所有事情发生,要么就什么都不发生。

  • C—一致性:我们相信 NoSQL 的新世界在这方面做得不好。我相信这意味着与 CAP 中的 C 相同。

  • I—隔离性:隔离性表示两个事务是串行的,这正是区块构造和链接所做的。

  • D—持久性:网络上的链接和复制确保如果一个或多个节点宕机,数据不会丢失。这就是为什么每个人都想带一个节点,以及为什么这些节点不应该不共存。

验证 – SSCs 签名和加密

安全服务容器(SSCs)中,软件、操作系统、虚拟化程序和 Docker 容器映像都不能被修改。证书可以包含在 SSC 中,以便它们可以自我验证为远程方的真实性。例如,在构建 SSC 时包含 SSL 证书有助于确保你正在与真实实例通话,因为 SSL 证书始终保护(加密)在 SSC 中。

使用 HSM

根据Wikipedia硬件安全模块(HSM)是一种物理计算设备,用于保护和管理用于强身份验证的数字密钥并提供加密处理。这些模块通常以插件卡或直接连接到计算机或网络服务器的外部设备的形式出现。

管理高安全性设备(如 HSM)可能是一个真正的挑战,因为与足够的安全性和控制相关。事实上,今天的标准规定了 HSM 管理(和密钥管理)系统的某些安全方法和级别。

摘要

在企业中采用区块链将需要权衡考虑。组织不仅需要运行、管理和维护其现有基础设施;它们还需要为这个承诺带来转型的新计算模型铺平道路。

在受监管的行业中,组织可能会面临合规成本的双重影响,因为即使是新技术平台也需要遵循已建立的监管框架和经过验证的技术架构标准和设计。考虑采用分层防御的理性方法,结合多种减轻安全风险的安全控制,有助于保护其资源和数据。采用分层防御方法,数字资产/智能合约以及分类数据将受到保护。

第二章:探索 Hyperledger Fabric

本章重点讨论 Hyperledger Fabric 项目——其组件、设计、参考架构以及企业就绪性。我们还将讨论由Linux FoundationLF)托管的 Hyperledger 项目的整体目标以及开源和开放标准的重要性。目标是建立对各种 Hyperledger 项目的多样性以及哪些框架和工具可能适合特定企业用例和软件消费模型的理解。尽管区块链技术领域不断变化,但 Hyperledger 项目代表着一种支持成熟和经过同行评审的技术结构,旨在供企业使用,并由多样化的人才和社区利益推动。

本章将涵盖以下主题:

  • Hyperledger 框架、工具和构建模块

  • Hyperledger Fabric 组件设计

  • Hyperledger Fabric——一笔交易的旅程

  • 探索 Hyperledger Fabric

  • 理解由区块链驱动的企业网络治理

Hyperledger 框架、工具和构建模块

现在我们已经看过 Hyperledger 在开放计算运动中的基础,以及它对行业的好处,让我们谈谈 Hyperledger 的框架、工具和构建模块。

Hyperledger 框架

以下是五个区块链框架:

  • Hyperledger Iroha:Iroha 专为移动开发项目设计,基于 Hyperledger Fabric,并由 Soramitsu、Hitachi、NTT Data 和 Colu 贡献。它具有现代的、面向领域驱动的 C++设计,以及一种名为Sumeragi的基于新链式拜占庭容错共识算法。

  • Hyperledger Sawtooth:Sawtooth 由 Intel 贡献,包括 Intel 提出的一种称为经过时间证明PoET)的新颖共识算法。 PoET 旨在尽可能有效地实现分布式共识。Hyperledger Sawtooth 在许多领域具有潜力,支持许可和无许可部署,并认识到各种需求。Sawtooth 被设计为多功能的。

  • Hyperledger Burrow:Hyperledger Burrow 最初由 Monax 和 Intel 贡献,是一个模块化的区块链,客户端构建了符合以太坊虚拟机EVM)规范的区块链。

  • Hyperledger FabricHLF):由 IBM 贡献的 Hyperledger Fabric 旨在成为以模块化架构开发应用程序或解决方案的基础。它允许插拔式组件,例如共识和成员服务,并利用容器托管称为链码的智能合约,构成系统的应用逻辑。本章余下部分将专注于 Hyperledger Fabric 及其设计、组件、架构和整体企业设计。

  • Hyperledger Indy:最初由 Sovrin 基金会贡献,Indy 是一个支持分布式账本上独立身份的 Hyperledger 项目。Hyperledger Indy 提供了工具、库和可重复使用的组件,用于提供基于区块链或其他分布式账本的数字身份:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

区块链工具

目前 Hyperledger 项目中还有五种工具,全部由 LF 托管。这些工具如下:

  • Hyperledger explorer:Hyperledger explorer 最初由 IBM、英特尔和 DTCC 贡献,可以查看、调用、部署或查询区块、交易和相关数据、网络信息(名称、状态、节点列表)、链码和交易族,以及存储在账本中的其他相关信息。

  • Hyperledger cello:Cello 也由 IBM 贡献。它旨在将按需即服务部署模型引入区块链生态系统,以减少创建、管理和终止区块链所需的工作量。Cello 可以在各种基础设施上高效、自动地提供多租户链服务,例如裸金属、虚拟机和其他容器平台。

  • Hyperledger composer:Hyperledger composer(由 IBM 和 Oxchains 贡献)是一组用于构建区块链业务网络的协作工具,可加速智能合约和区块链应用程序的开发,以及它们在分布式账本上的部署。

  • Hyperledger quilt:Hyperledger quilt 来自 NTT 数据和 Ripple,是 Ripple 的跨账本协议的 Java 实现,旨在在分布式和非分布式账本之间传输价值。

  • Hyperledger caliper:Caliper 是一个区块链基准测试工具,允许用户使用预定义的用例来衡量特定实现的性能,目前处于孵化状态,由众多组织的开发人员贡献。

区块链解决方案的构建基块

如第一章所述,区块链 - 企业和行业视角,区块链承诺在金融服务、供应链、物流和医疗保健等行业根本解决time信任问题。它旨在简化业务流程,从而解决低效率问题。它是基于信任、责任和透明度构建的新一代交易应用程序的技术。每个工业区块链都有几个共享特征,包括以下内容:

  • 共享的真实唯一数据源

  • 安全且防篡改

  • 私密不可链接身份

  • 可扩展的架构

  • 机密性

  • 可审计的

下图将这些特点总结为四个原则:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

区块链解决方案由四个基本构件组成——共享账本、隐私、信任和智能合约。让我稍微详细解释一下这些构件:

  • 共享账本:通过比特币区块链,其目的是实现可见性的民主化;然而,企业区块链由于消费者数据的监管而需要采用不同的方法。只追加不可变分布式事务记录可以通过 SQL 或无 SQL 分布式数据库来实现。

  • 加密保障隐私:通过加密保障隐私对于确保交易经过认证和验证至关重要。在区块链设计中加入加密技术是至关重要的,以加强安全性并增加突破分布式系统的难度。当您在使用较少民主化或经过许可的账本网络时,有关加密的考虑会发生变化。

  • 信任系统或共识:信任意味着利用网络的力量来验证交易。在任何区块链系统或应用中,信任都是至关重要的,我更喜欢使用信任系统而不是共识系统这一术语,因为信任是决定利益相关者投资于任何区块链基础设施的基础元素。每当新的参与者进入区块链领域并将区块链技术应用于新的用例或专业领域时,信任系统就会发生改变。信任模型真正是区块链的核心——它是传递信任交易所有权原则的基础。信任是使区块链能够取代交易系统的关键,但这只有在分布式/共享账本解决了交易和所有权时才能实现。对于各种用例,仍然需要大量工作来定义优化的信任系统。数据库解决方案正在努力解决规模和移动用例的问题,但在点对点和共享经济模型以及 B2B 模型方面还需要更多的工作。

  • 智能合约:在区块链的背景下,智能合约是嵌入到交易数据库中并随交易执行的商业协议。在业务中需要规则来定义价值流动和交易状态,这就是合同在这里的功能。合同之所以智能,是因为它是一个计算机协议,用来执行合同的条款。各种合同条款(如抵押、担保、财产权利的划定等)可以被编码,以强制执行对合同条款的遵守,并确保交易成功——这是智能合约背后的基本理念。智能合约的设计目的之一是让一方放心另一方将履行他们的承诺。这类合同的目标之一是降低验证和执行成本。智能合约必须是可观察的(意味着参与者可以看到或证明彼此的与合同相关的行动)、可验证的(意味着参与者可以向其他节点证明合同是否已执行或违约)、以及私密的(意味着合同内容/执行的了解应仅涉及执行它所需的必要参与者)。比特币为智能合约提供了相关规定;然而,它缺乏图灵完备性、缺乏状态等能力。以太坊通过构建一个内置图灵完备编程语言的区块链,改进了比特币的局限,因此任何人都可以编写智能合约和去中心化应用,创造自己的所有权、交易格式和状态转换函数的任意规则。这些进步使得复杂的合同可以在区块链中被编码,比如在飞行延误超过一定时间后即时向旅行者的银行账户转移信用,或者在实现绩效目标时支付员工补偿等。

    这在实际中如何工作?嗯,智能合约被部署为区块链节点上的代码,我们可能更恰当地称之为智能合约代码。这个代码是利用区块链技术来补充或替代现有的法律合同。这个智能合约代码是用 Solidity 或 Golang 等编程语言部署在区块链节点上的。在区块链上部署代码提供了三个重要的属性:

    • 区块链继承的永久性和抗审查性,

    • 程序本身控制区块链资产的能力,比如在参与者之间转移所有权或资产数量

    • 程序由区块链执行,确保始终按原样执行,没有人可以干扰

在企业世界中,智能合约可能涉及区块链的智能合约代码,以及更传统的法律合同。例如,智能合约代码可能在土地登记区块链网络上执行,将房屋所有权从一方转移到另一方,以便房屋登记记录实时更新,并且所有参与者(如城市、房地产经纪人、律师和银行)都可以在销售完成后更新自己的记录。但是,购房者将坚持要求具有补偿条款的法律合同,以 Cover any undiscovered liens。

Hyperledger Fabric 组件设计

让我们讨论一下促进区块链技术原则的各种组件,例如共享账本、加密、信任系统和智能合约。 这些组件代表了 Hyperledger Fabric 的基础设施组件,并且提供了与链码或智能合约开发构造的隔离。链码或智能合约开发细节将在单独的章节中详细讨论。

以下图示了 Hyperledger Fabric 基础设施组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hyperledger Fabric 基础设施组件

以下是基础设施组件:

  • Hyperledger Fabric CA 是成员服务的一个实现,但不是必须使用的(即,任何可发行 EC 证书的基于 X509 的 PKI 基础设施都可以使用)

  • 专用订购节点

    • 实现原子广播 API

    • 订购并批处理交易,并对每个批次(区块)进行签名以创建哈希链

    • Hyperledger Fabric 提供了两种实现——Solo(用于开发/测试)和基于 Kafka 的实现用于生产/容错

    • 订购服务是可插拔的——实施者只需根据 gRPC 接口定义提供原子广播 API

  • 节点现在负责现有的智能逻辑(链码)和维护分类帐

    • 背书模拟交易(即执行交易,但不提交交易)

    • 节点从订购节点接收批量背书的交易,然后验证和提交交易(这消除了不确定性)

Hyperledger 设计原则

再次强调,Hyperledger Fabric 是设计用于部署模块化和可扩展架构的区块链实现。它具有模块化子系统设计,以便随着时间的推移可以插入和实施不同的实现。本节介绍了 Hyperledger Fabric 参考架构,并描述了各种组件/模块及其交互和功能的详细信息。理解参考架构有助于更好地进行解决方案和技术设计决策,特别是在可扩展性、安全性和性能方面。

在本书中,我们将讨论 Hyperledger Fabric 的参考架构,请注意所有 Hyperledger 项目(先前提到的框架)遵循包括以下原则的设计理念:

  • 模块化和可扩展的方法:这意味着所有框架的所有组件都具有模块化。Hyperledger 为所有项目定义的组件包括(但不限于)以下内容:

    • 共识层

    • 智能合约(链码)层

    • 通信(八卦)层

    • 数据存储(持久性、日志和分类账数据)

    • 身份服务(信任根——用于识别参与者)

    • API

    • 可插拔加密

  • 互操作性:这一原则是关于向后互操作性,而不是关于各种 Hyperledger 项目驱动的区块链系统或业务网络之间的互操作性。

  • 专注于安全解决方案:企业及其业务网络的安全至关重要,因此专注于安全——不仅仅是加密抽象本身,而是组件之间的交互以及管理权限化区块链的结构。大多数开始使用权限化区块链的行业都是成熟且受监管的行业。

  • 代币(或硬币或加密资产)不可知的方法:这在治理部分进行了详细讨论,但 Hyperledger 项目不使用加密资产、加密货币、代币或类似硬币的构造作为建立信任系统的激励机制。虽然有一种资产代币化的概念,表示物理、虚拟或非物质化的资产,但资产代币化是一个与系统中生成的系统性代币截然不同的概念,后者是作为激励经济学的虚拟化而在系统中生成的。

  • 专注于丰富且易于使用的 API:这里的重点是确保区块链系统不仅具有企业中间件访问权限,还能够访问业务网络、现有参与者和新系统,而不暴露区块链驱动的业务网络的细节。

CAP 定理

2000 年在 ACM 分布式计算原理研讨会(PODC)上由埃里克·布鲁尔(Eric Brewer)提出的 CAP 定理(dl.acm.org/citation.cfm?id=343502)指出,在分布式数据存储中,不可能保证以下三个属性中的超过两个:一致性(C)、可用性(A)和分区容错性(P)。因此,分布式数据存储可以根据它保证的两个属性来描述,即 CA、CP 或 AP。

更具体地说,该定理旨在分布式系统部署在不可靠网络(例如存在故障和延迟的网络,如互联网)中,导致系统组件的分区。根据 CAP,在这些环境中,系统设计必须专注于可用性和一致性之间的平衡。例如,关系型数据库管理系统(RDBMS)通常提供的 ACID(原子性、一致性、隔离性、持久性)方法保证了单个节点上的一致性,但以牺牲跨多个节点的可用性(CP 类型系统)为代价。然而,需要注意的是,不同的配置可能会产生不同的组合,即 CA 或 AP。

相反,Fabric 与许多其他区块链平台一样,设计为 AP 类型系统,同时也采用了最终一致性,也被称为 BASE(基本可用、软状态、最终一致性)。

在区块链背景下,CAP 属性可以定义如下:

  • **一致性:**区块链网络避免了分类账的任何分叉

  • **可用性:**客户端提交的交易将永久写入分类账,并可在所有网络节点上使用。

  • **分区容错性:**即使区块链网络中出现任意数量的交易提案或区块在节点之间的物理网络介质中被丢弃(或延迟),网络仍然可以继续运行。

Fabric 实现了以下 CAP 属性:

  • **一致性:**通过使用 MVCC 对交易进行总序,并使用版本控制实现。

  • **可用性:**通过在每个节点上托管分类账的副本。

  • **分区容错性:**即使节点失败(达到阈值),也要保持运行。

如您所见,大多数区块链系统默认情况下保证了可用性和分区容错性(CAP 定理的 AP 属性)。然而,一致性更难提供。

Fabric 通过结合以下元素实现了一致性:

  • 交易处理被分割成一系列步骤,并跨网络的多个组件执行。

  • 客户端连接到通信渠道,并向背书节点提交交易提案,然后提交给排序服务。

  • 排序服务将交易按照总序排序成区块,即保证了整个网络上的交易顺序一致。一旦创建了区块,就会广播到通道的每个成员节点。广播协议保证了将区块以正确的顺序可靠地传递给节点,即总序广播。

  • 正如我们将在多版本并发控制中解释的那样,对等节点在接收到区块后,使用 MVCC 根据事务读取集中存储的键版本来验证每个事务。MVCC 验证保证了结果分类帐和世界状态的一致性,并防止了诸如双重支付之类的攻击。然而,它也可能导致被提交但顺序违反ReadSet版本验证检查的有效事务被消除。然后,在分类帐中将事务标记为有效或无效。

  • 然后,分类帐包含一系列完全有序的区块,其中每个区块包含一系列完全有序的事务(有效或无效),从而形成了对所有事务强制实施总顺序的分类帐。

Hyperledger Fabric 参考架构

Hyperledger Fabric 遵循模块化设计,以下是一些可能的组件或模块,可以插入和实现。请注意,此列表并不详尽:

  • 成员服务:此模块本质上是一个许可模块,并在网络创建过程中建立信任根,但这也是确保和管理成员身份的关键。成员服务本质上是一个证书颁发机构,同时利用了公钥基础设施PKI)的元素,用于诸如密钥分发、管理和建立随着网络增长而形成的联邦信任等方面。成员服务模块为区块链网络的成员发放证书提供了一个专用的数字证书颁发机构,并利用 Hyperledger Fabric 提供的加密功能。

  • 事务:事务是向区块链发出的执行分类帐上功能的请求。该功能由链码实现。通过将事务链接到先前的区块,并确保交易完整性(如果受保护),通过链接先前链接的区块中的加密信息或哈希,加密确保了交易的完整性。Hyperledger Fabric 中的每个通道都是其自己的区块链。

  • 智能合约或链码服务:链码是存储在分类帐上作为事务的一部分的应用级代码。链码运行可能修改世界状态的事务。事务逻辑以链码形式编写(使用 Go 或 JavaScript 语言),并在安全的 Docker 容器中执行。事务通过链码作用的通道上的数据进行转换。

这里是由链码服务启用的智能合约或链码元素。链码安装在对等节点上,需要访问资产状态以执行读取和写入操作。然后,链码在特定通道上针对特定对等节点进行实例化。通道中的分类帐可以在整个对等节点网络中共享,也可以仅包括特定的参与者集。对等节点能够参与多个通道:

  • 事件:验证对等方和链码的过程可能会在网络上产生事件(预定义事件和由链码生成的自定义事件),应用程序可以监听这些事件并采取行动。这些事件由事件适配器消耗,事件适配器可能使用 WebHooks 或 Kafka 等工具进一步传递事件。Fabric 提交对等方提供事件流以向已注册的监听器发布事件。截至 v1.0,唯一发布的事件是块事件。每当提交对等方向账本添加验证的区块时,都会发布一个块事件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 共识:共识是任何区块链系统的核心。它也实现了信任系统。一般来说,共识服务使得网络成员可以提出并验证数字签名的交易。在 Hyperledger Fabric 中,共识是可插拔的,并与 Hyperledger 提出的背书-排序-验证模型紧密关联。Hyperledger Fabric 中的排序服务代表共识系统。排序服务将多个交易打包成区块,并输出包含交易的哈希链接的区块序列。

  • 账本:另一个组件是分布式加密账本,包括只追加数据存储。这提供了在分布式账本上查询和写入数据的能力。有两个选择:

    • Level DB(默认嵌入式 KV 数据库)支持按键查询、复合键查询和键范围查询

    • Couch DB(外部选项)支持按键查询、复合键查询、键范围查询以及完整的数据丰富查询

  • 客户端 SDK:客户端 SDK 可以创建部署和调用共享账本上交易的应用程序。Hyperledger Fabric 参考架构支持 Node.js 和 Java SDK。软件开发工具包类似于编程工具包或一组工具,提供给开发人员编写和测试链码应用程序的库环境。SDK 在区块链应用程序开发中至关重要,并将在后续章节中进行详细讨论。SDK 包含的特定功能包括应用程序客户端、链码、用户、事件和加密套件。

Hyperledger Fabric 运行时架构

现在我们已经查看了参考架构,让我们考虑一下 Hyperledger Fabric 的运行时架构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下概述了 Hyperledger Fabric 运行时交易处理流程:

  • 交易提案(应用程序 SDK)

    1. 交易提案由应用程序 SDK 提交

    2. 它会收到交易提案响应(包括 ReadWrite 集)后的认可。

    3. 它将交易(包括 ReadWrite 集)提交给排序服务

  • 交易背书

    1. 交易被发送到其通道上代表背书对等方的交易对手

    2. 每个对等方通过调用指定的链码函数执行交易并签署结果,该结果成为交易的读写集

    3. 每个对等方可以参与多个通道,允许并发执行

  • 交易提交给排序服务

    1. 排序服务接受认可的交易,并根据插件共识算法对其进行排序,然后将其传递到通道上

    2. 通道上的对等方接收交易并在提交到分类账之前进行验证

  • 交易验证

    1. 验证每个交易并提交区块

    2. 验证认可策略

    3. 验证状态数据库中的 ReadSet 版本

    4. 将区块提交到区块链

    5. 将有效交易提交到状态数据库

分解设计的优势和优点

Hyperledger Fabric 的组件设计提供了几个优势。这些优势中的许多与业务网络治理有关,对于企业中的 Hyperledger Fabric 来说,这是一项重要的合规性和成本考虑。

这些好处包括以下内容:

  • 将开发设计与运行时设计区分开:分离开发和运行时设计很重要,因为这种区分对开发最佳实践和基础设施/混合云变体很重要,并确保遵守当前企业及其与业务网络应用开发的连接以及 DevOps 实践的连接。

  • 区分设计要求和基础设施/部署能力:组件化设计使我们能够将基础设施设计(包括网络连接、安全性、许可和合同工具等)与业务网络蓝图的整体应用设计分开,后者决定了技术蓝图。

  • 整合网络设计原则:Hyperledger Fabric 的模块化设计可以解决基础设施扩展问题,如连接数、协同位置、安全性、容器部署实践等。在网络设计方面有各种考虑因素,如云部署、混合和/或本地部署,以及任何可用选项的组合,这取决于业务网络中各个成员的需求。网络设计还解决了网络增长及其导致的性能和安全驱动的服务级别协议SLA)对其成员的挑战。

  • 解决频道设计原则:模块化或组件化设计也可以解决参与者之间的隔离、数据隐私和机密性,以及提供强大的审计能力的受控/许可访问。Hyperledger Fabric 中的频道结构使我们能够满足业务蓝图需求,实施可能是双边、三边甚至多边的业务定义交易。频道还提供了一种途径,限制交易数据的可见性仅限于少数参与者,或者在需要时提供全面访问权限,例如监管机构。频道设计还解决了交易处理、数据可见性、业务规则执行等关键业务需求。它还涉及技术影响,如可扩展性、安全性以及支持业务网络的基础设施成本。最后,频道设计解决了网络增长带来的业务挑战,以及为成员提供的性能和安全性驱动的服务水平协议。

  • 采用 Hyperledger Fabric Composer 模型驱动开发:Hyperledger Composer 是之前在 Hyperledger 工具中讨论的工具之一,为模块化开发提供了一种途径,使用便携、标准化的方式增加治理和控制,类似于 JEE 结构,如 JAR/WAR/RAR 等。业务网络存档BNA)是一种存档,可集成到 DevOps 实践中,用于跨企业团队开发和协作的生命周期管理能力。其理念是将链码开发与基础设施设计分开,将维护企业或业务网络应用程序技术实践这两个方面所需的能力分开。有关 Hyperledger Fabric Composer 的更多详细信息将在专门讨论 Composer 和工具的单独章节中介绍。

上述组件化设计的每一个优势在运行时/基础设施设计方面都有成本影响(即资源的使用和由此产生的成本)、灵活设计(例如产品和关系的变化)和解决方案的持久性(包括全球企业云基础设施的全球足迹,包括通过维护和支持形式的技术和业务专家的可靠访问)——所有这些对合规、治理和解决方案的持久性,以及由区块链驱动的业务网络至关重要。

Hyperledger Fabric——示例交易的过程

现在,让我们来看看使用 Hyperledger Fabric 的示例交易的过程。本节将帮助奠定 Hyperledger Fabric 概念和组件的基础,以便更好地理解交易处理所涉及的各个层面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hyperledger Fabric 演练

Fabric 引入了一种新设计的区块链,保留了交易处理架构,旨在实现安全、可扩展、弹性、模块化和保密的设计。Hyperledger Fabric(在撰写本书时,当前版本为 1.1)支持执行支持企业友好编程模型的分布式应用程序。Hyperledger Fabric 中的组件具有模块化设计,非常适合由各种企业组成的商业网络。Hyperledger Fabric 引入了基于三个步骤的模型,一个认可-排序-验证架构,旨在在不受信任的环境中执行不受信任代码的分布式执行。这种分离不仅允许规模的供应,还通过在每一层进行分离来确保安全性。

交易流程分为三个步骤,可以在系统中的不同实体上运行:

  1. 对交易的认可和检查其有效性验证步骤):这一步包括频道成员检查和遵守认可政策,这些政策定义了验证交易提案的可接受的协商方式。由于对等方需要更新分类帐(在交易确定性之后),因此订阅频道的对等方会审核提案并提供其分类帐版本的(R)读取和(W)写入集。这一验证步骤非常重要,因为它为交易验证提供了第一步。这一检查也起到了门禁的作用,防止了交易的计算错误下游处理,这可能具有计算成本昂贵的风险。

  2. 通过排序服务进行排序:这是一种共识协议,旨在可插拔,不考虑交易语义。共识的可插拔性为企业和商业网络提供了巨大的灵活性,因为有各种行业、用例和网络参与者之间的交互的共识机制考虑因素。

  3. 验证或交易提交:这意味着提交交易,因此需要按照应用程序特定的信任假设进行一系列最终验证。

Hyperledger Fabric 交易涉及三种类型的节点:

  • 提交对等方是维护分类帐和状态的节点。提交对等方是提交交易并可能持有智能合约或链代码的一方。

  • 认可对等方是一个专业的提交对等方,可以批准或否决交易提案的认可。认可对等方必须持有智能合约。

  • 排序节点(服务)与提交对等方节点进行通信;它们的主要功能是批准交易块被纳入分类帐。与提交对等方和认可对等方不同,排序节点不持有智能合约或分类帐。

验证可以分为两种角色,认可和排序:

  • 签署交易意味着验证它是否遵守智能合约;背书人签署合同以完成这个验证方面

  • 订购验证要包含到分类帐中的交易;这种形式的验证有助于控制录入分类帐的内容并确保其一致性

那么链码调用呢?在 Hyperledger Fabric 交易中,模拟(链码执行)和区块验证/提交是分开的。

执行链码操作(换句话说,进行业务交易)涉及三个阶段;

  1. 第一阶段是通过背书对等方的模拟执行链码操作。可以启用背书者的并行模拟以帮助提高并发性和可伸缩性,因为模拟不会更新区块链状态。

  2. 接下来,模拟确定业务交易提案,即读取集/写入集,并将其广播到订购服务。

  3. 然后,交易提案根据其他交易进行排序,并广播到提交对等方(包括背书对等方),他们验证其读取集在模拟后未被修改,并自动应用其写入集。

通道也是交易过程中的一个重要方面,因为对等方通过通道以共识方式交换消息,并确保不同分类帐之间的隐私。以下是关于通道的一些注意事项:

  • 所有节点都不必连接到它们

  • 对等方通过访问控制策略连接到通道

  • 订购服务对通道广播的交易进行排序

  • 对等方按通道以完全相同的顺序接收交易

  • 交易以加密链接的区块形式交付

  • 每个对等方都会验证交付的区块并将其提交到分类帐

探索 Hyperledger Fabric

区块链网络中的参与者:区块链是一个基于网络的基础设施,其中应用网络中心化的设计、开发、部署、管理和支持构建。因此,了解与区块链网络进行交互以进行管理、支持、业务用户、监管等各种目的的各种参与者及其角色非常重要:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个参与者都有一个角色和入口点,并定义了一个有助于网络治理、审计和合规要求的治理结构。业务网络治理(以下点中详细介绍)是一项重要的合规和成本考虑因素。用户是区块链的用户方。他们创建和分发区块链应用,并使用区块链执行操作。这些参与者是一致的,并基于 ISO/IEC 17788 的云计算参与者和角色:

  • 开发人员:区块链开发人员是为用户(客户端)创建应用程序的参与者,并开发与区块链交互的智能合约(服务器端),然后由区块链用户使用以启动交易。他们还编写代码以使区块链能够与传统应用程序交互。

  • 管理员:区块链管理员执行管理活动,如部署和配置区块链网络或应用程序。

  • 运营商:区块链运营商负责定义、创建、管理和监控区块链网络和应用。

  • 审计员:区块链审计员负责审查区块链交易,并从业务、法律、审计和合规性的角度验证其完整性。

  • 业务用户:此术语指在业务网络中操作的用户。他们使用应用程序与区块链交互,但可能不知道区块链,因为它将是一个不可见的交易系统。

区块链网络中的组件

一般来说,区块链系统由许多节点组成,每个节点都有分类帐的本地副本。在大多数系统中,节点属于不同的组织。节点彼此通信以就分类帐内容达成一致。

获得这种一致性的过程称为共识,为此已经开发了许多不同的算法。用户向区块链发送交易请求以执行链设计的操作。一旦交易完成,交易记录将被添加到一个或多个分类帐中,并且永远不能被更改或删除。区块链的这种特性称为不可变性。密码学用于保护区块链本身以及区块链系统的各个元素之间的通信。它确保分类帐不能被更改,除非添加新的交易。密码学确保来自用户或节点之间的消息的完整性,并确保操作仅由授权实体执行:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在区块链上执行交易的权限可以采用两种模式之一:有权限或无权限。在有权限的区块链中,用户必须在允许执行交易前加入区块链。加入过程会给予用户凭证,这些凭证在用户执行交易时用于标识用户。在无权限的区块链中,任何人都可以执行交易,但通常被限制只能对自己的数据执行操作。区块链所有者开发了一个可执行的软件模块称为智能合约,它被安装到区块链中。当用户向区块链发送交易时,它可以调用智能合约模块,该模块执行由智能合约模块创建者定义的功能。

开发人员互动

Hyperledger Fabric Explored章节的介绍中所述,区块链开发人员可以担任多种角色,包括为用户创建应用程序(客户端)和开发智能合约。开发人员还编写代码,使区块链能够与传统应用程序交互:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

区块链开发人员的主要角色是创建应用程序(和集成)和智能合约以及它们与企业网络和参与者的分类账和其他企业系统的交互。由于 Hyperledger Fabric 基础设施的分离,基础设施构造(如对等方、共识、安全、通道、策略)和开发人员领导的活动(如智能合约开发、部署、企业集成、API 管理和前端应用程序开发)之间存在明确的分离。

从开发人员的角度来看,以下概述代表了开发人员与 Hyperledger Fabric 构造交互的一个例子:

  • 开发人员创建应用程序和智能合约

  • 应用程序可以通过 SDK 调用智能合约内部的调用。

  • 这些调用通过内置于智能合约中的业务逻辑和各种命令和协议来处理:

    • putdelete命令将经过所选的共识协议,并将被添加到区块链中。

    • get命令只能从世界状态中读取,但不会记录在区块链上。

  • 应用程序可以使用诸如get block height的 rest API 访问块信息。

请注意在此处使用了 delete——delete 可以从世界状态数据库中删除密钥,但不能删除区块链上的交易,因为我们已经确定这些交易是不可变的。

以下图表总结了所有关键角色:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

理解区块链驱动的商业网络中的治理

治理可以定义为中心化或分散式机构,其唯一责任是在给定系统中建立一套规则或法律,以作出约束性决定。在区块链网络中,治理带来一系列挑战,在本节中,我们想讨论这些挑战以及区块链网络中的治理结构。在区块链的背景下,治理的话题呈现出一个有趣的悖论。

当一个区块链网络被创建时,治理结构通常是分布式的,由各方利益相关者提供输入。区块链网络以去中心化和自我治理为特征,具有内置的控制点和激励机制,以帮助维持适当的平衡。交易经过一系列去中心化的处理步骤,决策作为输出提供交易的最终确定性。这种治理结构基于激励经济学和共识。

区块链始于主要为无许可网络(例如,基于加密资产的网络,如比特币、莱特币等)的网络,依靠基于技术的系统治理通过激励和协调。当企业尝试应用区块链原则时,这种系统治理在商业世界中会带来一些挑战。企业世界受到严格监管,因此依靠具有制衡的许可区块链模型;考虑到各种数据法规、受托责任和正在进行交易的竞争实体之间的潜在利益冲突,这可能变得相当复杂。由于机密性和隐私问题,不能必然存在相同类型的激励或协调。

企业的重点通常是了解区块链技术及其对业务的潜在影响。治理现在已经成为企业区块链世界中一个有趣的新兴学科——也是一个重要的学科。从区块链商业模型的讨论中可以看出,存在各种可能的治理结构,从完全去中心化和准去中心化到完全集中化的区块链网络。治理结构实际上决定了区块链采用的许多其他方面,从设计到运营再到增长模型。商业模型和治理结构密切相连且相互依存;两者都直接影响区块链网络运作的各个方面。

治理结构和格局

那种依靠网络参与者之间的激励和协调的系统治理对于解决更多受监管行业及其用例是不够的。因此,我试图为更传统的企业定义一个治理结构和格局,这是一种利用现有最佳实践的模块化方法。

这个模型旨在促进进步和增长,但提供了网络参与者的必要分离。我将概述的简化治理结构建立在区块链的核心原则以及激励、惩罚、灵活性、委托和协调的原则之上。请记住,利用区块链的目标是发展信任网络,同时强制执行某些参与规则。一般来说,区块链项目的目标是激励技术和安全的升级,并惩罚不遵守规定的行为,希望确保继续参与和共享区块链网络带来的商业利益。我再次描述的业务治理模型不仅有助于在这样的网络中公平参与,还有助于公平的成本结构。本节提供了一个高层次的背景。我们在专门讨论治理的章节中讨论了更多细节。

信息技术治理

IT 治理的学科专注于 IT 基础设施、性能、成本结构和风险。在分散的区块链网络中,这会带来一些挑战,因为治理框架应建立问责制,以鼓励良好行为和网络 IT 基础设施的最佳运行。区块链网络的技术设计和基础设施选择应能够适应参与者的需求。由于区块链网络在至少某种程度上是分散的,IT 治理应包括分布式灵活性和分布式控制。

IT 治理应至少提供以下内容:

  • 分布式 IT 管理结构

  • 分布式维护、升级等模型

  • 利用行业标准——COBIT、ITIL、ISO、CMMI、FAIR 等

  • 资源优化——这包括技术采购、供应商关系、SLA 管理、技能和人才管理

  • 技术采用和评估以跟上技术演进

  • 网络部署策略,以鼓励和强制定期更新和升级

  • 网络支持服务——IT SLA 执行和会员服务

  • 风险优化——运营支持服务OSSs)和业务支持服务BSSs),IT 基础设施连续服务/规划,技术与法律和法规要求的对齐,等等

区块链网络治理

治理可以涉及以下内容:

  • 管理参与网络

  • 形成公平的成本结构,根据参与者的活动公平分配

  • 允许志同道合的参与实体参与交易和价值创造

  • 管理参与规则和社会契约,以促进公平

区块链网络治理包括以下内容:

  • 吸纳和退出成员

  • 建立公平的成本结构

  • 详细说明数据所有权的工作方式

  • 监管监督和合规报告

  • 管理具有中央管理和投票流程的许可结构,联邦结构和委派结构

  • 管理业务运营和 SLA

  • 网络支持服务(与 IT 治理相同)

  • 风险优化(与 IT 治理相同)

商业网络治理

管理由区块链支持的业务网络将需要一个特定于用例和行业的模型,考虑该行业的发展和特殊情况。这种治理结构将是多组织的,并且参与组织需要通过他们的集体贡献对网络功能有广泛的理解,以实现最佳结果。随着新的参与者被添加或移除,并且区块链网络发展,其动态也会改变。

共创的概念意味着将各方聚集在一起,产生互利和有价值的结果。一个例子可以是将一家公司与一群客户团结在一起,产生新的想法,并听取新的观点。

以下是商业网络治理可能包括的非尽量列表:

  • 制定商业模式,网络运作规则和法律章程

  • 网络中通用的共享的服务管理,比如了解你的客户流程,审计,报告等

  • 与网络相关的沟通

  • 质量保证和绩效测量

  • 监控和管理网络安全

  • 产品和业务网络发展计划

  • 法律和监管框架的执行

  • 确保符合行业特定要求的策略

  • 设立技术和网络的管理者

区块链网络的治理结构可能是一个有趣的挑战。正如我所展示的,关于区块链网络的完全去中心化,准去中心化和完全中心化仍然存在着相当大的辩论,这实际上取决于治理结构。我的意思是,区块链网络的治理结构有助于决定什么样的互动,发展,技术选择和运营最适合该网络。正如我之前所述,区块链是一个能够实现共创的平台,从中产生的新的协同效应将需要通过 SLAs 和健全的治理结构进行一些管理。治理将在第十章中进行详细讨论,治理,受监管行业的必要之恶

总结

所有这些都有助于吸引新的网络参与者,以及维持创始和现有参与者的信心,同时保持商业利益和价值。

业务模型和治理结构相互依赖,以正确管理区块链网络的运作。一个精心策划的治理模型将确保涉及的实体之间的和谐,它们可能在不同时间充当竞争者、共同创造者或合作者。

第三章:以业务场景为背景设定舞台

前两章着重于搭建一个区块链项目的舞台,并定义了一个业务框架和各种 Hyperledger 项目如何解决时间和信任问题。

通过了解构成 Hyperledger Fabric 的组件,我们现在将深入研究应用设计和实施方面的考虑。接下来的几章将带您完成创建自己的智能合约的步骤,然后将其集成到一个应用程序中。

为了使这些练习相关,我们将利用一个源自一些古老文明的业务用例:贸易和信用证。

本章的目标是介绍信用证的业务概念,带您走过我们选择的样例场景,并通过设置我们的开发环境来结束。

在本章中,我们将:

  • 探索信用证

  • 回顾我们简化的业务场景

  • 设置我们的开发环境

贸易和信用证

退回到历史的某个时刻,当商人穿越大陆购买一国的布料去另一个国家出售时。作为佛罗伦萨的羊毛商人,你可能会前往阿姆斯特丹购买这个新成立的城邦的优质羊毛,该港口汇集了整个北欧乃至更远地区的资源。然后你可以将羊毛运到佛罗伦萨,在那里可以卖给为富有客户制作精美服装的裁缝。我们谈论的是公元 1300 年——这是一个携带黄金或其他贵重金属作为货币购买和出售商品不安全的时代。必需的是一种跨越国界的货币形式,可以在阿姆斯特丹和佛罗伦萨以及任何地方使用!

马可·波罗曾去过赛里斯,看到了那个繁荣经济中的商业活动是如何进行的。在成功的可汗帝国的核心是我们今天会认识到的先进金融技术。法定货币、纸币、本票和信用证都是通过赛里斯传入欧洲的。马可·波罗将这些想法带回了欧洲——它们帮助形成并发展了罗马帝国灭亡后新兴的欧洲的一个商业银行业。

信任在促进贸易中的重要性

现在我们的佛罗伦萨商人可以联系他的银行家,告诉他他想在阿姆斯特丹购买羊毛,银行将以账上付款的方式给他出具一张信用证。这封信可以有各种规定,例如交易的最大金额、如何支付(一次性或分期付款)、可以用于哪些商品等。商人现在将前往阿姆斯特丹,在羊毛商选择羊毛后,他将提供信用证作为付款。阿姆斯特丹商人会高兴地将羊毛与信用证交换,因为当涉及到金钱时,佛罗伦萨银行家在整个欧洲都以信誉良好而闻名。阿姆斯特丹商人可以将信用证带给他的银行家,后者将会给他的账户记入贷方。当然,佛罗伦萨和阿姆斯特丹的银行家都会向各自的客户——商人——收取这项服务的费用!这对每个人都有好处。

定期,阿姆斯特丹银行家和佛罗伦萨银行家会见面来结算账户,但这对于羊毛贸易商和羊毛商人来说毫无意义。事实上,发生的是佛罗伦萨和阿姆斯特丹商人利用各自银行家之间的信任来建立彼此之间的信任关系——这是一个非常复杂的想法,当你想一想就会明白。这就是为什么信用证流程至今仍然是全球业务的基本方式。

今日信用证流程

然而,随着贸易的大规模全球化和金融行业的爆炸性增长,参与信用证流程的金融机构数量激增!如今,可能有超过 20 家中介金融机构参与其中。这需要协调许多人和系统,从而导致整个过程对商家和银行都产生了过多的时间、成本和风险。

区块链的承诺是提供一个逻辑上单一但物理上分布式的系统,为低摩擦的信用证流程提供平台。这样的系统特征将包括更大的透明度、及时性和自动化(导致成本降低),以及增量支付等新特性。

商业场景和用例

国际贸易包括那些说明了区块链旨在减轻的现实世界流程中的低效率和不信任的情况。因此,我们选择了一个进口-出口场景的元素,其中包含在接下来几章的实际练习中进行的简化版本的交易作为我们的规范用例。

概述

我们将描述的情景涉及一笔简单的交易:从一方向另一方出售货物。由于买方和卖方住在不同的国家,这笔交易变得复杂,因此没有共同的可信介质来确保出口商得到承诺的款项以及进口商得到货物。今天世界上的贸易安排依赖于以下内容:

  • 促成付款和货物实际转移的中介

  • 随着时间的推移已发展出来的流程,以使出口商和进口商对冲风险并降低风险

现实世界中的流程

促进支付和货物实际转移的中间人是出口商和进口商的各自银行。在这种情况下,贸易安排通过银行与其客户之间以及两家银行之间的信任关系来实现。这些银行通常具有国际联系和声誉需要维护。因此,进口商银行的承诺(或承诺)向出口商银行付款足以触发流程。出口商从出口国政府获得监管清关许可后,通过知名的国际承运人发运货物。

向承运人交付的交付证明足以使进口商银行向出口商银行清算付款,这种清算不取决于货物是否到达预定目的地(假设货物在运输过程中已经购买了丢失或损坏的保险)。进口商银行向出口商银行做出的支付承诺指定所需的一系列文件作为发运证明,以及立即或分期付款的具体支付方式。出口商必须在交付货物给承运人之前满足各种监管要求,以获得文件清关许可。

简化和修改过的流程

我们的用例将遵循前述流程的简化版本,带有某些变化以展示区块链在便利贸易方面的价值。进口商的银行向出口商的银行承诺支付两期付款。出口商从监管机构获得清关证书,将货物交由承运人,然后获得收据。收据的出具触发进口商银行向出口商银行支付第一期付款。当货物到达目的港口时,进行第二和最后一期的支付,流程结束。

贸易金融和物流中使用的术语

以下术语用于指代贸易场景中正在使用的某些工具和物品。我们在本章中构建的应用程序使用这些工具的非常简化形式:

  • 信用证:正如我们在本章开头所见,这指的是银行承诺在出具货物装运的文件证明后向出口商付款。简称 L/C,此文件由进口商的银行根据其客户(进口商)的要求出具。L/C 列明了构成装运证明的文件清单、要支付的金额以及受益人(在我们的案例中为出口商)的金额。下图显示了一份样本 L/C:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们将在我们的使用案例中引入一些小的变化,以使读者能够理解此工具。首先,信用证将由出口商的银行开具,而不是直接由出口商开具。其次,信用证规定支付将分两期进行,第一期在出具两份文件后支付,第二期在货物到达目的地后支付。

  • 出口许可证:指出口国监管机构对指定货物的运输所给予的批准。在本书中,我们将其简称为 E/L。下图显示了一份样本 E/L:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 提单:这是承运人在接收货物后向出口商出具的文件。简称 B/L,它同时起到收据、合同(约束承运人将货物运送到指定目的地以换取报酬)、货物所有权证明的作用。该文件也列在信用证中,并作为装运证明,将自动触发付款结清。下图显示了一份样本 B/L:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

共享流程工作流程

本章节中呈现的每一个测试用例场景都需要很长时间才能完成,涉及不同时间段不同集合实体之间的互动,并且有许多不同的移动部分,很难跟踪。我们希望使用我们的工作流程来简化这个过程。在区块链上实施的交易序列描述如下步骤(并在下图中说明)可以以不可撤销和不可否认的方式进行。在这一事件序列中,我们假设一个笔直的、线性的叙述,各方互相达成协议,没有发生任何意外事件;过程中只内置了一些用于捕捉错误的保障。

我们工作流程中的交易如下所示:

  1. 进口商请求出口商以货物换取货款

  2. 出口商接受交易协议

  3. 进口商向其银行要求开具一份有利于出口商的信用证

  4. 进口商的银行向出口商提供一份有利于出口商且可支付给后者银行的 L/C

  5. 出口商的银行代表出口商接受信用证

  6. 出口商向监管机构申请 E/L

  7. 监管机构向出口商提供 E/L

  8. 出口商准备了一批货物并交给承运人

  9. 承运人在验证 E/L 后接受货物,然后向出口商提供 B/L

  10. 出口商的银行向进口商的银行索取一半支付款

  11. 进口商的银行将一半金额转账给出口商的银行

  12. 承运人将货物运送到目的地

  13. 进口商的银行向出口商的银行支付剩余金额

这是一个解释交易流程的图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

共享资产和数据

上一个工作流程中的参与者必须有一些共同的信息,让他们能够随时查看交易安排及其进展。

以下是参与者拥有的资产表,这些资产与其他人共享,驱动流程从一阶段到下一阶段。这包括文件和货币资产:

资产类型资产属性
信用证ID、发行日期、到期日期、发行方、受益人、金额和文件列表
提单ID、发货人(出口商)、收货人(进口商)、通知方(进口商的银行)、收发货地点、货物描述和运费金额
出口许可证ID、发行日期、到期日期、受益人、许可证持有者和货物描述
付款标准货币单位金额

以下是界定参与者在每个阶段可选择的选择的数据元素:

数据类型数据属性
贸易协议进口商请求,出口商接受
信用证进口商请求,进口商银行发行,出口商银行接受
出口许可证请求者是出口商,由监管机构发行
货物运输出口商准备,承运人接受,当前位置或地点

参与者的角色和能力

在我们的场景中有六类参与者:出口商、进口商、出口商银行、进口商银行、承运人和监管机构。这个集合中的术语指的是实体在贸易交易中可以承担的角色;例如,一家公司在一次出口货物交易中可能是进口商。每个角色的能力和限制也在以下列表中详细说明:

  • 只有进口商可以申请 L/C

  • 只有进口商的银行可以提供 L/C

  • 只有出口商的银行可以接受 L/C

  • 只有出口商才能请求 E/L

  • 只有监管机构才能提供 E/L

  • 只有出口商可以准备货物

  • 只有承运人可以提供 B/L

  • 只有承运人可以更新货物位置

  • 只有进口商的银行可以汇款,只有出口商的银行可以接收款项

区块链应用的优势超过当前现实世界的流程

在缺乏保障的情况下转移货物或进行支付所固有的风险(如缺乏可信的调解者)催生了银行的参与,并导致信用证和提单的产生。这些过程的一个结果不仅仅是额外的成本(银行要收取佣金来发行信用证),或者额外的开支。申请和等待授予出口许可证也会增加周转时间。在理想的贸易场景中,只有准备和运输货物的过程需要时间。最近,采用 SWIFT 消息传递比手动通信更高效,但并没有从根本上改变游戏规则。另一方面,区块链几乎即时的交易承诺和保证拓展了以前不存在的可能性。

作为示例,我们在我们的应用场景中引入的一个变化是分期付款,这在传统框架中无法实现,因为没有一种可靠的方式来了解和共享关于货物进展的信息。在这种情况下,这样的变化被认为是太过风险,这就是为什么支付纯粹与文件证据相关联。通过让交易协议中的所有参与方在一个共同的区块链上实施一个公共智能合同,我们可以提供一个共享的真实性来源,从而最小化风险,同时增加问责制。

在接下来的章节中,我们将详细演示我们的应用是如何在 Hyperledger Fabric 和 Composer 平台上实现的。读者将能够欣赏实现的简单性和优雅性,然后可以将其用作指南,来重塑其他应用的古老流程,利用这一激动人心的新技术。然而,在跳入代码之前,我们将研究 Hyperledger 网络的设计,并设置我们的开发环境。

设置开发环境

正如你现在已经知道的那样,Hyperledger Fabric 区块链的一个实例被称为通道,它是以加密方式相互关联的交易日志。要设计和运行区块链应用程序,第一步是确定需要多少个通道。对于我们的贸易应用程序,我们将使用一个通道,它将维护不同参与方之间进行的交易历史记录。

一个 Fabric 节点可能属于多个通道,从应用程序的角度来看,这些通道彼此毫无所知,但它们帮助单个节点代表其所有者(或客户)在不同应用程序中运行交易。一个通道可以运行多个智能合约,每个智能合约可以是独立的应用程序,或者链接在一起形成多合约应用程序。在本章和本书中,我们将为读者介绍一个简单的单通道、单合约应用程序的设计。读者可以根据本书提供的信息以及 Fabric 文档设计更复杂的应用程序。

在我们深入了解如何设置系统以安装应用程序并在智能合约上运行交易的机制之前,我们将描述如何创建和启动一个网络,该网络将安装应用程序。本章将使用一个样本网络结构来说明贸易操作(在第九章,区块链网络中的生活,您将看到如何根据需求的变化和发展修改此样本网络)。

设计网络

为了确定一个 Hyperledger Fabric 网络结构以供应用程序使用,第一步是列出参与的组织。在逻辑上,一个组织是一个安全域和身份与凭证的单位。它管理一个或多个网络节点,并依赖于一个成员服务提供商MSP)为节点和智能合约访问权限的客户颁发身份和证书。订购服务是 Fabric 网络的基石,通常被分配给一个独立的组织。下图说明了一个典型的节点网络结构,其中包括客户端、MSP 和逻辑组织分组。

交易(或调用)批准的标准是一个背书策略(我们将在本章后面重新讨论)。它是根据参与应用程序网络的组织来制定的,而不是节点本身。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3.1:区块链网络,节点分布在各个组织之间,客户端从组织获取凭证,以提交查询和调用链代码

必须提前决定一组节点、它们所属的组织以及为每个组织提供服务的成员服务提供商,以便在这些机器上安装和运行适当的服务。

我们的示例贸易网络将由四个组织组成,分别代表出口商、进口商、承运商和监管机构。后两者分别代表承运商和监管实体。然而,出口商组织既代表出口实体又代表其银行。同样地,进口商组织代表进口实体和其银行。将信任的实体与其交易方分组到单一组织中,从安全和成本的角度来看都是有意义的。运行一个 Fabric 节点是一个沉重且昂贵的业务,因此让拥有更多资源和大量客户的银行代表自身和其客户运行这样的节点是足够的。一个贸易实体在其组织中以客户端的角色获得提交交易或读取账本状态的权利。因此,我们的区块链网络需要四个同行,各自属于不同的组织。除了同行,我们的网络包括每个组织的一个 MSP,以及以单独模式运行的排序服务。

在生产应用中,排序服务应该作为 Zookeeper 上的 Kafka 集群设置,但为了展示如何构建区块链应用程序,排序服务可以被视为一个黑盒子。

排序服务属于自己的独立组织,带有一个 MSP。我们贸易网络的组织、其 MSP、同行和客户端在下面的图表中展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3.2:一个贸易网络,包括同行、排序者和各自组织中的客户端。

读者可能会想,如果一个贸易方和其银行属于同一个组织,那么应用程序如何区分两者(出口商和出口商的银行,以及进口商和进口商的银行),以控制对智能合约和账本的访问。这可以通过以下两种方式实现:

  • 嵌入中间件和应用层(我们将在本章后面描述)中的访问控制逻辑,用户可以通过其 ID(或登录名)区分,并维护将 ID 映射到允许的链码功能的访问控制列表。

  • 让一个组织的 MSP 作为 CA 服务器,嵌入其为组织成员颁发的证书中的区别属性。访问控制逻辑可以在中间件或链码中实现,以解析属性,并根据应用程序政策允许或拒绝操作。

这些机制没有在我们的应用中实现,其中银行家和客户对智能合约和中间件层来说是无法区分的。但读者可以将其视为一种练习,对于熟练于开发安全客户端-服务器应用程序的人来说,这应该是直截了当的。

安装先决条件。

在手持网络设计的情况下,让我们安装先决工具:

  1. 确保您拥有最新版本的:

  2. 我们将使用 GitHub 来共享教程的源代码。要访问 GitHub,需要安装 Git 客户端,并配置身份验证到 GitHub。有关更多信息,请访问 GitHub 的官方网站help.github.com/articles/set-up-git/

  3. 安装业务网络示例所需的软件:hyperledger.github.io/composer/latest/installing/installing-prereqs

    上述说明适用于 Mac 和 Linux。请注意,在使用 Windows 时,我们建议使用像 Vagrant 这样的解决方案在虚拟机中运行开发环境。

  4. Fabric 是用 Go 语言实现的。请注意:

    • Go 在语法上与 C++ 类似。

    • 我们还将使用 Go 来编写链代码。

    • Go 可以从golang.org/安装。

请注意,本书中的 Hyperledger Fabric 设置和教程应用程序的测试是使用 Go 1.9 完成的,因此建议读者安装并使用 1.9 或更高版本。

  1. 接下来,我们需要设置环境变量。

GOPATH 指向go源代码的工作空间,例如:

         $ export GOPATH=$HOME/go 

PATH 需要包括用于存储库和可执行文件的 Go bin 目录,如下面的代码片段所示:

         $ export PATH=$PATH:$GOPATH/bin 
  1. 验证系统上是否安装了 make。在 Debian/Ubuntu 系统上,您可以使用 sudo apt-get install make 安装它。

分叉并克隆贸易融资物流存储库

现在,我们需要通过在 GitHub 上分叉存储库来获取原始源代码的副本。然后,我们可以使用以下步骤将源代码克隆到本地机器目录中:

  1. 在 GitHub 中导航到以下存储库github.com/HyperledgerHandsOn/trade-finance-logistics

  2. 分叉存储库:使用页面右上角的分叉按钮创建源代码的副本到您的帐户

  3. 获取克隆 URL:导航到贸易融资物流存储库的您的分叉。单击“克隆或下载”按钮,并复制 URL。

  4. 克隆存储库:在 Go 工作空间中,按以下方式克隆存储库:

$ cd $GOPATH/src 
$ git clone https://github.com/YOUR-USERNAME/trade-finance-logistics

现在我们有了所有贸易融资物流教程材料的本地副本。

创建和运行网络配置

配置和启动我们网络的代码可以在我们存储库的 network 文件夹中找到(这是 fabric-samples/first-network 的一个改编)。在本次练习中,我们将在单个物理或虚拟机上运行整个网络,各种网络元素在适当配置的 Docker 容器中运行。假设读者对使用 Docker 进行容器化和使用 Docker-compose 进行配置有基本的了解。一旦满足前一节中列出的先决条件,只需运行该节中的命令,无需读者具备额外的知识或配置。

准备网络

要构建 Fabric 和 Fabric-CA,如果缺少某些依赖项,您可能需要安装某些依赖项。这些包括 gcclibtoolltdl 库。(在 Ubuntu Xenial 系统上,可以通过运行 sudo apt-get install libltdl-dev 安装所有必要的先决条件。读者需要在其他系统上寻找相应的等效物)。在生成网络加密材料之前,我们需要执行以下步骤。

教程应用是在 Hyperledger Fabric 版本 1.1 上开发的,因此您需要获取和构建该版本的组件。

  1. 克隆 Fabric (github.com/hyperledger/fabric/tree/release-1.1) 源代码存储库。如果您使用 git clone 命令,请添加参数 -b release-1.1。确保克隆的 fabric 文件夹存在于 $GOPATH/src/github.com/hyperledger/ 中,或者在该路径中创建符号链接。当您尝试构建 Fabric 时,它将在此路径中查找库。

  2. 运行 make docker 来为对等方和排序者构建 Docker 镜像。

  3. 运行 make configtxgen cryptogen 来生成运行本节描述的网络创建命令所需的必要工具。

  4. 克隆 Fabric-CA (github.com/hyperledger/fabric-ca/tree/release-1.1) 源代码存储库。(如果您使用 git clone 命令,请添加参数 -b release-1.1。确保克隆的 fabric-ca 文件夹存在于 $GOPATH/src/github.com/hyperledger/ 中,或者在该路径中创建符号链接。当您尝试构建 Fabric-CA 时,它将在此路径中查找库。

  5. 运行 make docker 来构建 MSPs 的 Docker 镜像。

生成网络加密材料

配置网络的第一步涉及为每个对等方和订购方组织的 MSP 以及基于 TLS 的通信创建证书和签名密钥。我们还需要为每个对等方和订购方节点创建证书和密钥,以便能够彼此通信以及与各自的 MSP 通信。这个配置必须在我们的代码库中的network文件夹中的crypto-config.yaml文件中指定。该文件包含组织结构(稍后在通道工件配置部分中更多细节),每个组织中对等方的数量以及必须为其中的每个用户创建证书和密钥的默认数量(请注意,默认情况下会创建一个admin用户)。例如,请参阅文件中 Importer 组织的定义如下:

PeerOrgs:
- Name: ImporterOrg
  Domain: importerorg.trade.com
  EnableNodeOUs: true
  Template:
    Count: 1
  Users:
    Count: 2

此配置指示标记为ImporterOrg的组织将包含一个对等方。还将创建两个非管理员用户。还定义了对等方要使用的组织域名。

要为所有组织生成加密材料,请执行以下cryptogen命令:

cryptogen generate --config=./crypto-config.yaml

输出保存在crypto-config文件夹中。

生成通道工件

为了按照组织的结构创建网络,并引导一个通道,我们需要生成以下工件:

  • 初始块,包含用于初始化 Fabric 区块链的组织特定证书。

  • 通道配置信息。

  • 每个组织的锚定对等配置。锚定对等在组织内部充当支点,使用 Fabric 八卦协议进行跨组织账本同步。

crypto-config.yaml文件类似,通道属性在一个名为configtx.yaml的文件中指定,在我们的源代码中可以在network文件夹中找到。我们贸易网络的高级组织如下所示:

Profiles:
  FourOrgsTradeOrdererGenesis:
    Capabilities:
      <<: *ChannelCapabilities
    Orderer:
      <<: *OrdererDefaults
      Organizations:
        - *TradeOrdererOrg
      Capabilities:
        <<: *OrdererCapabilities
    Consortiums:
      TradeConsortium:
        Organizations:
          - *ExporterOrg
          - *ImporterOrg
          - *CarrierOrg
          - *RegulatorOrg
  FourOrgsTradeChannel:
    Consortium: TradeConsortium
    Application:
      <<: *ApplicationDefaults
      Organizations:
        - *ExporterOrg
        - *ImporterOrg
        - *CarrierOrg
        - *RegulatorOrg
      Capabilities:
        <<: *ApplicationCapabilities

正如我们所看到的,我们要创建的通道被命名为FourOrgsTradeChannel,在配置文件中定义。参与此通道的四个组织被标记为ExporterOrgImporterOrgCarrierOrgRegulatorOrg,每个组织都引用了Organizations部分中定义的子部分。订购方属于自己的组织称为TradeOrdererOrg。每个组织部分包含有关其 MSP 的信息(ID 以及加密材料的位置,如密钥和证书),以及其锚定对等方的主机名和端口信息。例如,ExporterOrg部分包含以下内容:

- &ExporterOrg
  Name: ExporterOrgMSP
  ID: ExporterOrgMSP
  MSPDir: crypto-config/peerOrganizations/exporterorg.trade.com/msp
  AnchorPeers:
    - Host: peer0.exporterorg.trade.com
    Port: 7051

正如你所看到的,这个规范中的MSPDir变量(表示一个文件夹)引用了我们之前使用cryptogen工具生成的加密材料。

要生成通道文件,我们使用configtxgen工具。要生成创世区块(将在网络引导期间发送到 orderer),请从network文件夹运行以下命令:

configtxgen -profile FourOrgsTradeOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

FourOrgsTradeOrdererGenesis关键字对应于Profiles部分中的配置文件名称。创世区块将保存在channel-artifacts文件夹中的genesis.block文件中。要生成通道配置,请运行以下代码:

configtxgen -profile FourOrgsTradeChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID tradechannel

我们将创建的通道名为tradechannel,其配置存储在channel-artifacts/channel.tx中。要生成出口者组织的锚定 peer 配置,请运行:

configtxgen -profile FourOrgsTradeChannel -outputAnchorPeersUpdate ./channel-artifacts/ExporterOrgMSPanchors.tx -channelID tradechannel -asOrg ExporterOrgMSP

对于其他三个组织,应重复相同的过程,并在先前的命令中更改组织名称。

必须将环境变量FABRIC_CFG_PATH设置为指向包含configtx.yaml文件的文件夹,以便configtxgen工具正常工作。我们稍后将使用的trade.sh脚本文件包含以下行以确保从运行命令的文件夹加载YAML文件:

export FABRIC_CFG_PATH=${PWD}

在一个操作中生成配置

为了方便起见,trade.sh脚本已配置为使用先前描述的命令和配置文件生成通道文件以及加密材料。只需从network文件夹中运行以下命令:

./trade.sh generate -c tradechannel

虽然您可以在这里指定任何通道名称,但请注意,本章后面用于开发中间件的配置将依赖于该名称。

GOPATH变量在运行 peer 的容器中设置为/opt/gopath

组合一个样例交易网络

最后一个命令还具有生成网络配置文件docker-compose-e2e.yaml的效果,该文件用于使用 docker-compose 工具在一组 Docker 容器中启动网络。文件本身依赖于静态配置文件base/peer-base.yamlbase/docker-compose-base.yaml。这些文件共同指定服务及其属性,并使我们能够一次性在 Docker 容器中运行它们,而不是在一个或多个机器上手动运行这些服务的实例。我们需要运行的服务如下:

  • 四个 Fabric peer 实例,每个组织一个

  • 一个 Fabric orderer 实例

  • 五个 Fabric CA 实例,对应于每个组织的 MSP

每个都可以从 Docker Hub 上的 Hyperledger 项目(hub.docker.com/u/hyperledger/)获取 Docker 镜像,其中镜像分别为hyperledger/fabric-peerhyperledger/fabric-ordererhyperledger/fabric-ca for peersorderersMSPs

一个 peer 的基本配置可以如下(参见base/peer-base.yaml):

peer-base:
image: hyperledger/fabric-peer:$IMAGE_TAG
environment:
  - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
  - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_trade
  - CORE_LOGGING_LEVEL=INFO
  - CORE_PEER_TLS_ENABLED=true
  - CORE_PEER_GOSSIP_USELEADERELECTION=true
  - CORE_PEER_GOSSIP_ORGLEADER=false
  - CORE_PEER_PROFILE_ENABLED=true
  - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
  - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
  - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start

Fabric 配置参数可以在此设置,但如果您使用fabric-peer的预构建 Docker 映像,则默认设置足以运行对等方服务。在配置的最后一行指定运行对等方服务的命令为peer node start;如果您希望通过下载 Fabric 源代码并在本地机器上构建来运行对等方,则这是您将要运行的命令(有关示例,请参见Chapter 4使用 Golang 设计数据和事务模型)。还请确保使用CORE_LOGGING_LEVEL变量适当配置日志级别。在我们的配置中,该变量设置为INFO,这意味着仅记录信息、警告和错误消息。如果您希望调试对等方并需要更广泛的日志记录,请将此变量设置为DEBUG

IMAGE_TAG变量在network文件夹中的.env文件中设置为 latest,尽管如果您希望拉取旧的映像,可以设置特定的标签。

此外,我们需要为每个对等方配置主机名和端口,并将使用cryptogen生成的加密材料同步到容器文件系统。导出器组织中的对等方在base/docker-compose-base.yaml中配置如下:

peer0.exporterorg.trade.com:
  container_name: peer0.exporterorg.trade.com
  extends:
    file: peer-base.yaml
    service: peer-base
  environment:
    - CORE_PEER_ID=peer0.exporterorg.trade.com
    - CORE_PEER_ADDRESS=peer0.exporterorg.trade.com:7051
    - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.exporterorg.trade.com:7051
    - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.exporterorg.trade.com:7051
    - CORE_PEER_LOCALMSPID=ExporterOrgMSP
  volumes:
    - /var/run/:/host/var/run/
    - ../crypto-config/peerOrganizations/exporterorg.trade.com/peers/peer0.exporterorg.trade.com/msp:/etc/hyperledger/fabric/msp
    - ../crypto-config/peerOrganizations/exporterorg.trade.com/peers/peer0.exporterorg.trade.com/tls:/etc/hyperledger/fabric/tls
    - peer0.exporterorg.trade.com:/var/hyperledger/production
  ports:
    - 7051:7051
    - 7053:7053

正如extends参数所示,这扩展了基本配置。注意,ID(CORE_PEER_ID)与configtx.yaml中为此对等方指定的 ID 相匹配。该标识是运行在导出器组织中的对等方的主机名,并且稍后在本章的中间件代码中将使用它。卷部分指示将在crypto-config文件夹中生成的加密材料复制到容器的规则。对等方服务本身侦听端口7051,客户端用于订阅事件的端口设置为7053

在文件中,您会看到容器内端口在对等方之间是相同的,但映射到主机机器上的端口是不同的。最后,请注意,此处指定的 MSP ID 也与configtx.yaml中指定的相匹配。

订购者服务的配置类似,如base/docker-compose-base.yaml中的以下片段所示:

orderer.trade.com:
  container_name: orderer.trade.com
  image: hyperledger/fabric-orderer:$IMAGE_TAG
  environment:
    - ORDERER_GENERAL_LOGLEVEL=INFO
  ……
  command: orderer
  ……

启动订购者的命令很简单,就像代码所示的那样是orderer。日志级别可以使用ORDERER_GENERAL_LOGLEVEL变量进行配置,在我们的配置中设置为INFO

我们将要运行的实际网络配置是基于一个名为docker-compose-e2e.yaml的文件。这个文件并不在存储库中,而是通过我们先前运行的./trade.sh generate -c tradechannel命令创建的。这个文件依赖于base/docker-compose-base.yaml(间接地也依赖于base/peer-base.yaml),你可以通过检查文件内容来看到。它实际上是从一个名为docker-compose-e2e-template.yamlYAML模板文件创建的,你可以在network文件夹中找到。模板文件包含变量作为cryptogen生成的关键文件的代理。当docker-compose-e2e.yaml被生成时,这些变量名称会被实际文件名替换,这些文件存在于crypto-config文件夹中。

例如,考虑docker-compose-e2e-template.yaml中的exporter-ca部分:

exporter-ca:
  image: hyperledger/fabric-ca:$IMAGE_TAG
  environment:
    ……
    - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/EXPORTER_CA_PRIVATE_KEY
  ……
  command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.exporterorg.trade.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/EXPORTER_CA_PRIVATE_KEY -b admin:adminpw -d'

现在,看看在生成的文件docker-compose-e2e.yaml中的相同部分:

exporter-ca:
  image: hyperledger/fabric-ca:$IMAGE_TAG
  environment:
    ……
    - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/ cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk
  ……
  command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.exporterorg.trade.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk -b admin:adminpw -d'

如你所见,环境变量和命令中的变量EXPORTER_CA_PRIVATE_KEY已被替换为cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk。如果你现在检查crypto-config文件夹的内容,你会注意到在crypto-config/peerOrganizations/exporterorg.trade.com/ca/文件夹中存在一个名为cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk的文件。这个文件包含了出口商组织 MSP 的私(秘密)签名密钥。

前述的代码片段包含了一个样本运行的结果。每当你运行加密材料生成工具时,关键文件名将会有所变化。

让我们现在更详细地看一下 MSP 的配置,以出口商组织 MSP 为例,如docker-compose-e2e.yaml中所指定的那样:

exporter-ca:
  image: hyperledger/fabric-ca:$IMAGE_TAG
  environment:
    - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
    - FABRIC_CA_SERVER_CA_NAME=ca-exporterorg
    - FABRIC_CA_SERVER_TLS_ENABLED=true
    - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.exporterorg.trade.com-cert.pem
    - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk
  ports:
    - "7054:7054"
  command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.exporterorg.trade.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/cc58284b6af2c33812cfaef9e40b8c911dbbefb83ca2e7564e8fbf5e7039c22e_sk -b admin:adminpw -d'
  volumes:
    - ./crypto-config/peerOrganizations/exporterorg.trade.com/ca/:/etc/hyperledger/fabric-ca-server-config
  container_name: ca_peerExporterOrg
  networks:
    - trade

将在 MSP 中运行的服务是fabric-ca-server,监听端口7054,使用cryptogen创建的证书和密钥进行引导,并使用fabric-ca镜像中配置的默认登录名和密码(分别为adminadminpw)。启动一个 Fabric CA 服务器实例的命令是fabric-ca-server start ...,如你在前述代码中所看到的。

同样,对等方和 CA 都配置为基于 TLS 的通信,正如前述配置所示。读者必须注意,如果一个地方禁用了 TLS,另一个地方也必须禁用 TLS。

同样,通过检查docker-compose-e2e.yaml,我们可以看到,我们没有为订购方的组织创建 Fabric CA 服务器(和容器)。对于在本书中要进行的练习,静态创建的管理用户和订购者的凭据足够了;我们不会动态注册新的订购者组织用户,所以不需要 Fabric CA 服务器。

网络组件的配置文件

我们已经演示了如何在 docker-compose YAML 文件中配置对等方、订购者和 CA。但是这些配置意味着覆盖了组件的默认设置。虽然这些配置的详细说明超出了本书的范围,但我们将列出各自的文件并提及用户如何对其进行更改。

对于一个对等方,一个名为 core.yaml 的文件(github.com/hyperledger/fabric/blob/release-1.1/sampleconfig/core.yaml)包含了所有重要的运行时设置,包括但不限于地址、端口号、安全和隐私以及八卦协议。您可以创建自己的文件并使用自定义 Dockerfile 将其同步到容器中,而不是使用默认的 hyperledger/fabric-peer 镜像。如果您登录到一个正在运行的对等方容器(让我们从我们刚刚启动的网络中取出 Exporter 组织的对等方容器):

docker exec -it f86e50e6fc76 bash

然后您将在文件夹 /etc/hyperledger/fabric/ 中找到 core.yaml 文件。

类似地,订购者的默认配置位于一个名为 orderer.yaml 的文件中(github.com/hyperledger/fabric/blob/release-1.1/sampleconfig/orderer.yaml),该文件也与运行 hyperledger/fabric-orderer 镜像的容器中的 /etc/hyperledger/fabric/ 同步。请注意,core.yamlorderer.yaml 文件都同步到对等方和订购者容器,因此,如果您希望创建自定义文件,则需要将这些 YAML 文件同步到这两个容器。

Fabric CA 服务器还有一个名为 fabric-ca-server-config.yaml 的配置文件(hyperledger-fabric-ca.readthedocs.io/en/latest/serverconfig.htm),它与运行 hyperledger/fabric-ca 镜像的容器中的 /etc/hyperledger/fabric-ca-server/ 同步。您可以像为对等方或订购者创建和同步自定义配置一样创建和同步自定义配置。

启动示例交易网络

因此,现在我们已经有了网络的所有配置,以及运行所需的通道工件和加密材料,我们所需做的就是使用 docker-compose 命令启动网络,如下所示:

docker-compose -f docker-compose-e2e.yaml up

您可以将此作为后台进程运行,并将标准输出重定向到 log 文件,如果您愿意的话。否则,您将看到各种容器启动和每个容器的日志显示在控制台上。

请注意,在某些操作系统配置中,设置 Fabric 可能会有些棘手。如果遇到问题,请查阅文档。有关如何安装 Fabric 网络以及示例的详细说明,请访问 hyperledger-fabric.readthedocs.io/en/release-1.1/samples.html

网络也可以使用我们的 trade.sh 脚本在后台启动;只需运行:

./trade.sh up

从另一个终端窗口,如果您运行 docker ps -a,您将看到以下内容:

CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES
4e636f0054fc    hyperledger/fabric-peer:latest    "peer node start"    3 minutes ago    Up 3 minutes    0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp    peer0.carrierorg.trade.com
28c18b76dbe8    hyperledger/fabric-peer:latest    "peer node start"    3 minutes ago    Up 3 minutes    0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp    peer0.importerorg.trade.com
9308ad203362    hyperledger/fabric-ca:latest    "sh -c 'fabric-ca-se..."    3 minutes ago    Up 3 minutes    0.0.0.0:7054->7054/tcp    ca_peerExporterOrg
754018a3875e    hyperledger/fabric-ca:latest    "sh -c 'fabric-ca-se..."    3 minutes ago    Up 3 minutes    0.0.0.0:8054->7054/tcp    ca_peerImporterOrg
09a45eca60d5    hyperledger/fabric-orderer:latest    "orderer"    3 minutes ago    Up 3 minutes    0.0.0.0:7050->7050/tcp    orderer.trade.com
f86e50e6fc76    hyperledger/fabric-peer:latest    "peer node start"    3 minutes ago    Up 3 minutes    0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp    peer0.exporterorg.trade.com
986c478a522a    hyperledger/fabric-ca:latest    "sh -c 'fabric-ca-se..."    3 minutes ago    Up 3 minutes    0.0.0.0:9054->7054/tcp    ca_peerCarrierOrg
66f90036956a    hyperledger/fabric-peer:latest    "peer node start"    3 minutes ago    Up 3 minutes    0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp    peer0.regulatororg.trade.com
a6478cd2ba6f    hyperledger/fabric-ca:latest    "sh -c 'fabric-ca-se..."    3 minutes ago    Up 3 minutes 0.0.0.0:10054->7054/tcp    ca_peerRegulatorOrg

我们有四个对等节点、四个 MSP 和一个运行在不同容器中的订购者。我们的交易网络已经启动并准备运行我们的应用程序!

要查看给定容器的运行日志,请注意容器 ID(上述列表中的第一列),然后简单运行:

docker logs <container-ID>

若要关闭网络,您可以使用 docker-compose 命令:

docker-compose -f docker-compose-e2e.yaml down

或者我们的 trade.sh 脚本:

./trade.sh down

总结

在本章中,我们介绍了我们后续章节将利用的业务用例,以便为我们将要编写的代码创建一个上下文。我们还部署了我们的第一个 Hyperledger Fabric 网络,现在已经从理论转向实践。干得好!

下一章将从两个角度带您了解区块链应用程序的开发:(1)使用链码和 Fabric SDK 的基础 API (2)使用 Hyperledger Composer 的业务网络实现。

通过这两个角度,我们希望能让您了解解决方案的灵活性以及在正确环境中利用每个工具的能力。要为下一章做好准备,您现在应该使用 ./trade.sh down 停止您的网络。

第四章:用 Golang 设计数据和交易模型

在超级账本 Fabric 中,链代码是由开发人员编写的智能合约的一种形式。链代码实现了区块链网络的利益相关者所达成一致的业务逻辑。该功能对客户端应用程序开放,以便它们调用,前提是它们具有正确的权限。

链代码作为一个独立的进程在自己的容器中运行,与 Fabric 网络的其他组件隔离开来。背书节点负责管理链代码和交易调用的生命周期。作为对客户端调用的回应,链代码查询和更新分类账,并生成交易提案。

在本章中,我们将学习如何用 Go 语言开发链代码,并实现场景的智能合约业务逻辑。最后,我们将探讨开发完全功能的链代码所必需的关键概念和库。

在接下来的章节中,我们将探索与概念相关的代码片段,你可以在以下地址找到链代码的完整实现:

github.com/HyperledgerHandsOn/trade-finance-logistics/tree/master/chaincode/src/github.com/trade_workflow_v1

请注意,这也可以在我们在上一章中创建的本地 git 克隆中找到。我们有两个版本的链代码,一个在trade_workflow文件夹中,另一个在trade_workflow_v1文件夹中。我们需要两个版本来演示稍后升级,在第九章区块链网络的生活中。在本章中,我们使用v1版本来演示如何用 Go 编写链代码。

在本章中,我们将讨论以下主题:

  • 创建链代码

  • 访问控制

  • 实现链代码函数

  • 测试链代码

  • 链代码设计主题

  • 记录输出

启动链代码开发

在我们可以开始编写链代码之前,我们首先需要启动我们的开发环境。

设置开发环境的步骤已在第三章使用商业情景设定舞台中解释过。然而,我们现在继续启动开发模式下的 Fabric 网络。这种模式允许我们控制如何构建和运行链代码。我们将使用此网络在开发环境中运行我们的链代码。

下面是我们如何以开发模式启动 Fabric 网络的步骤:

$ cd $GOPATH/src/trade-finance-logistics/network
$ ./trade.sh up -d true  

如果在启动网络时遇到任何错误,可能是由一些残留的 Docker 容器引起的。

你可以通过停止网络使用./trade.sh down -d true,然后运行以下命令解决这个问题:./trade.sh clean -d true

-d true 选项告诉我们的脚本在开发网络上采取行动。

我们的开发网络现在在五个 Docker 容器中运行。网络由单个订购者、在devmode中运行的单个对等体、一个链码容器、一个 CA 容器和一个 CLI 容器组成。CLI 容器在启动时创建了一个名为tradechannel的区块链通道。我们将使用 CLI 与链码交互。

随时查看日志目录中的日志消息。它列出了网络启动期间执行的组件和函数。我们将保持终端开放,因为一旦安装并调用了链码,我们会在这里收到进一步的日志消息。

编译和运行链码

克隆源代码已经使用 Go vendoring 包含了所有的依赖关系。考虑到这一点,我们现在可以开始构建代码,并使用以下步骤运行链码:

  1. 编译链码:在一个新的终端中,连接到链码容器并使用以下命令构建链码:
$ docker exec -it chaincode bash 
$ cd trade_workflow_v1 
$ go build 
  1. 使用以下命令运行链码:
$ CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=tw:0 ./trade_workflow_v1  

我们现在有一个连接到对等体的运行中的链码。这里的日志消息表明链码已经启动并正在运行。您还可以在网络终端中检查日志消息,其中列出了对等体上与链码的连接。

安装和实例化链码

在我们启动通道之前,现在需要在通道上安装链码,这将调用Init方法:

  1. 安装链码:在一个新的终端中,连接到 CLI 容器并按如下方式安装名称为tw的链码:
$ docker exec -it cli bash 
$ peer chaincode install -p chaincodedev/chaincode/trade_workflow_v1 -n tw -v 0
  1. 现在,实例化以下链码
$ peer chaincode instantiate -n tw -v 0 -c '{"Args":["init","LumberInc","LumberBank","100000","WoodenToys","ToyBank","200000","UniversalFreight","ForestryDepartment"]}' -C tradechannel 

CLI 连接的终端现在包含了与链码交互的日志消息列表。链码终端显示了来自链码方法调用的消息,而网络终端显示了对等体和订购者之间通信的消息。

调用链码

现在我们有一个正在运行的链码,我们可以开始调用一些函数。我们的链码有几种创建和检索资产的方法。目前,我们只会调用其中两个;第一个是创建一个新的交易协议,第二个是从分类账中检索它。要做到这一点,请完成以下步骤:

  1. 使用以下命令在分类账上放置具有唯一 IDtrade-12的新交易协议:
$ peer chaincode invoke -n tw -c '{"Args":["requestTrade", "trade-12", "50000", "Wood for Toys"]}' -C tradechannel
  1. 使用以下命令从分类账中检索 ID 为trade-12的交易协议:
$ peer chaincode invoke -n tw -c '{"Args":["getTradeStatus", "trade-12"]}' -C tradechannel

现在,我们在devmode中有一个正在运行的网络,我们已成功测试了我们的链码。在接下来的部分,我们将学习如何从头开始创建和测试链码。

开发模式

在生产环境中,链码的生命周期由对等体管理。当我们需要在开发环境中重复修改和测试链码时,我们可以使用devmode,它允许开发人员控制链码的生命周期。此外,devmodestdoutstderr标准文件重定向到终端;这些在生产环境中是禁用的。

要使用 devmode,对等方必须连接到其他网络组件,就像在生产环境中一样,并且以参数 peer-chaincodedev=true 启动。然后,链代码将单独启动并配置为连接到对等方。在开发过程中,可以从终端重复编译、启动、调用和停止链代码。

在接下来的部分中,我们将使用启用了 devmode 的网络。

创建链代码

现在我们已经准备好开始实现我们的链代码了,我们将使用 Go 语言编程。有几个提供对 Go 的支持的 IDE 可用。其中一些较好的 IDE 包括 Atom、Visual Studio Code 等等。无论您选择的环境如何,都可以与我们的示例一起使用。

链代码接口

每个链代码都必须实现 Chaincode interface,其方法是在接收到交易提案后调用的。SHIM 包中定义的 Chaincode interface 如下所示:

type Chaincode interface { 
    Init(stub ChaincodeStubInterface) pb.Response 
    Invoke(stub ChaincodeStubInterface) pb.Response 
} 

正如你所看到的,Chaincode 类型定义了两个函数:InitInvoke

这两个函数都有一个参数,类型为 ChaincodeStubInterface,名为 stub

stub 参数是我们在实现链代码功能时将使用的主要对象,因为它提供了访问和修改账本、获取调用参数等功能。

另外,SHIM 包提供了其他类型和函数来构建链代码;你可以在以下链接检查整个包:godoc.org/github.com/hyperledger/fabric/core/chaincode/shim.

设置链代码文件

现在让我们设置 chaincode 文件。

我们将使用从 GitHub 克隆的文件夹结构进行工作。链代码文件位于以下文件夹中:

$GOPATH/src/trade-finance-logistics/chaincode/src/github.com/trade_workflow_v1

您可以按照步骤检查文件夹中的代码文件,也可以创建一个新文件夹并按描述创建代码文件。

  1. 首先,我们需要创建 chaincode 文件

在您喜欢的编辑器中,创建一个名为 tradeWorkflow.go 的文件,并包含以下包和导入语句:

package main

import (
    "fmt"
    "errors"
    "strconv"
    "strings"
    "encoding/json"
    "github.com/hyperledger/fabric/core/chaincode/shim"
    "github.com/hyperledger/fabric/core/chaincode/lib/cid"
    pb "github.com/hyperledger/fabric/protos/peer"
)

在前面的代码片段中,我们可以看到第 4 至 8 行导入了 Go 语言系统包,第 9 至 11 行导入了 shimcidpb Fabric 包。pb 包提供了对 peer protobuf 类型的定义,而 cid 则提供了访问控制函数。在访问控制部分,我们将更详细地了解 CID。

  1. 现在我们需要定义 Chaincode 类型。让我们添加将实现链代码函数的 TradeWorkflowChaincode 类型,如下所示:
type TradeWorkflowChaincode struct {
    testMode bool
}

注意第 2 行中的 testMode 字段。我们将在测试期间使用此字段来绕过访问控制检查。

  1. TradeWorkflowChaincode 是实现 shim.Chaincode 接口所需的。必须实现接口的方法,以使 TradeWorkflowChaincode 成为 shim 包中 Chaincode 类型的有效实现。

  2. Init 方法在将链码安装到区块链网络后调用。每个认可节点只会执行一次,部署其自己的链码实例。此方法可用于初始化、引导和设置链码。Init 方法的默认实现如下代码片段所示。请注意,第 3 行中的方法向标准输出写入一行以报告其调用。第 4 行中,方法返回了对 shim 函数调用的结果。成功的参数值为 nil 表示成功执行并返回空结果,如下所示:

// TradeWorkflowChaincode implementation
func (t *TradeWorkflowChaincode) Init(stub SHIM.ChaincodeStubInterface)         pb.Response {
    fmt.Println("Initializing Trade Workflow")
    return shim.Success(nil)
}

调用链码方法必须返回 pb.Response 对象的一个实例。以下代码片段列出了两个来自 SHIM 包的辅助函数,用于创建响应对象。以下函数将响应序列化为 gRPC protobuf 消息:

// Creates a Response object with the Success status and with argument of a 'payload' to return
// if there is no value to return, the argument 'payload' should be set to 'nil'
func shim.Success(payload []byte)

// creates a Response object with the Error status and with an argument of a message of the error
func shim.Error(msg string)
  1. 现在是移步到调用参数的时候了。在这里,该方法将使用 stub.GetFunctionAndParameters 函数检索调用的参数,并验证是否提供了预期数量的参数。Init 方法希望收到零个参数,因此将账本保持为原样。当 Init 函数被调用时,这是因为链码在账本上升级到新版本。当链码首次安装时,它希望收到包含参与者详情的八个参数,这些将被记录为初始状态。如果提供了不正确数量的参数,该方法将返回错误。验证参数的代码块如下:
_, args := stub.GetFunctionAndParameters()
var err error

// Upgrade Mode 1: leave ledger state as it was
if len(args) == 0 {
  return shim.Success(nil)
}

// Upgrade mode 2: change all the names and account balances
if len(args) != 8 {
 err = errors.New(fmt.Sprintf("Incorrect number of arguments. Expecting 8: {" +
             "Exporter, " +
             "Exporter's Bank, " +
             "Exporter's Account Balance, " +
             "Importer, " +
             "Importer's Bank, " +
             "Importer's Account Balance, " +
             "Carrier, " +
             "Regulatory Authority" +
             "}. Found %d", len(args)))
  return shim.Error(err.Error())
}

正如我们在上述代码片段中所见,当提供了包含参与者姓名和角色的预期参数数量时,该方法会验证和将参数转换为正确的数据类型,并将它们作为初始状态记录到账本中。

在下面的代码片段中,在第 2 和第 7 行,该方法将参数转换为整数。如果转换失败,它返回错误。在第 14 行,通过字符串常量构造了一个字符串数组。这里,我们引用了在 chaincode 文件夹中的 constants.go 文件中定义的词法常量,这些常量代表了初始值将被记录到账本中的键。最后,在第 16 行,对于每个常量,一个记录(资产)被写入到账本中。stub.PutState 函数记录了一个键值对到账本中。

请注意,账本上的数据以字节数组形式存储;我们想要存储在账本上的任何数据都必须首先转换为字节数组,正如您在以下代码片段中所见:

// Type checks
_, err = strconv.Atoi(string(args[2]))
if err != nil {
    fmt.Printf("Exporter's account balance must be an integer. Found %s\n", args[2])
    return shim.Error(err.Error())
}
_, err = strconv.Atoi(string(args[5]))
if err != nil {
    fmt.Printf("Importer's account balance must be an integer. Found %s\n", args[5])
    return shim.Error(err.Error())
}

// Map participant identities to their roles on the ledger
roleKeys := []string{ expKey, ebKey, expBalKey, impKey, ibKey, impBalKey, carKey, raKey }
for i, roleKey := range roleKeys {
    err = stub.PutState(roleKey, []byte(args[i]))
    if err != nil {
        fmt.Errorf("Error recording key %s: %s\n", roleKey, err.Error())
        return shim.Error(err.Error())
    }
}

调用方法

每当查询或修改区块链的状态时,都会调用Invoke方法。

所有对账本上持有的资产的创建读取更新删除CRUD)操作都由Invoke方法封装。

当事务由调用客户端创建时,将调用此方法。当查询状态时(即,检索一个或多个资产但不修改账本的状态时),上下文事务将在客户端接收到Invoke响应后被丢弃。一旦账本已被修改,修改将被记录到事务中。在接收到要记录在账本上的事务的响应后,客户端将提交该事务给一个排序服务。下面的代码片段显示了一个空的Invoke方法:

func (t *TradeWorkflowChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    fmt.Println("TradeWorkflow Invoke")
}

通常,链码的实现将包含多个查询和修改函数。如果这些函数非常简单,可以直接在Invoke方法的主体中实现。然而,更加优雅的解决方案是独立实现每个函数,然后从Invoke方法中调用它们。

SHIM API 提供了几个函数来检索Invoke方法的调用参数。这些列在以下代码片段中。开发人员可以选择参数的含义和顺序;但是,通常情况下,Invoke方法的第一个参数是函数的名称,其后的参数是该函数的参数。

// Returns the first argument as the function name and the rest of the arguments as parameters in a string array.
// The client must pass only arguments of the type string.
func GetFunctionAndParameters() (string, []string)

// Returns all arguments as a single string array.
// The client must pass only arguments of the type string.
func GetStringArgs() []string

// Returns the arguments as an array of byte arrays.
func GetArgs() [][]byte

// Returns the arguments as a single byte array.
func GetArgsSlice() ([]byte, error)

在下面的代码片段中,第 1 行使用stub.GetFunctionAndParameters函数检索调用的参数。从第 3 行开始,一系列if条件将执行与参数一起传递到请求的函数(requestTradeacceptTrade等)。每个这些函数都独立实现其功能。如果请求了一个不存在的函数,该方法将返回一个错误,指示请求的函数不存在,如第 18 行所示:

    function, args := stub.GetFunctionAndParameters()

    if function == "requestTrade" {
        // Importer requests a trade
        return t.requestTrade(stub, creatorOrg, creatorCertIssuer, args)
    } else if function == "acceptTrade" {
        // Exporter accepts a trade
        return t.acceptTrade(stub, creatorOrg, creatorCertIssuer, args)
    } else if function == "requestLC" {
        // Importer requests an L/C
        return t.requestLC(stub, creatorOrg, creatorCertIssuer, args)
    } else if function == "issueLC" {
        // Importer's Bank issues an L/C
        return t.issueLC(stub, creatorOrg, creatorCertIssuer, args)
    } else if function == "acceptLC" {
  ...

  return shim.Error("Invalid invoke function name")

如您所见,Invoke方法是放置任何需要用于提取和验证将由请求的函数使用的参数的共享代码的合适位置。在下一节中,我们将看看访问控制机制,并将一些共享访问控制代码放入Invoke方法中。

访问控制

在深入讨论Chaincode函数的实现之前,我们需要首先定义我们的访问控制机制。

安全且受权限限制的区块链的关键特点是访问控制。在 Fabric 中,成员服务提供商MSP)在启用访问控制方面发挥了关键作用。Fabric 网络的每个组织都可以有一个或多个 MSP 提供者。MSP 实现为证书颁发机构Fabric CA)。有关 Fabric CA 的更多信息,包括其文档,请访问:hyperledger-fabric-ca.readthedocs.io/.

Fabric CA 为网络用户颁发注册证书ecerts)。ecert 代表用户的身份,并在用户提交给 Fabric 时用作签名交易。在调用交易之前,用户必须首先从 Fabric CA 注册并获得 ecert。

Fabric 支持一种基于属性的访问控制ABAC)机制,链码可以使用该机制控制对其功能和数据的访问。ABAC 允许链码根据与用户身份关联的属性做出访问控制决策。拥有 ecert 的用户也可以访问一系列附加属性(即,名称/值对)。

在调用期间,链码将提取属性并做出访问控制决策。我们将在即将到来的章节中更深入地了解 ABAC 机制。

ABAC

在接下来的步骤中,我们将向您展示如何注册用户并创建具有属性的 ecert。然后我们将在链码中检索用户身份和属性,以验证访问控制。然后我们将把这个功能集成到我们的教程链码中。

首先,我们必须使用 Fabric CA 注册一个新用户。在注册过程中,我们必须定义生成 ecert 后将使用的属性。通过运行命令fabric-ca-client register来注册用户。访问控制属性可以使用后缀:ecert添加。

注册用户

这些步骤仅供参考,无法执行。更多信息可以参考 GitHub 存储库github.com/HyperledgerHandsOn/trade-finance-logistics/blob/master/chaincode/abac.md

现在让我们注册一个具有自定义属性名为importer和值为true的用户。请注意,属性的值可以是任何类型,并不仅限于布尔值,如下段所示:

fabric-ca-client register --id.name user1 --id.secret pwd1 --id.type user --id.affiliation ImporterOrgMSP --id.attrs 'importer=true:ecert'

前面的片段显示了注册具有属性importer=true的用户时的命令行。请注意,id.secret的值和其他参数取决于 Fabric CA 配置。

上述命令还可以一次定义多个默认属性,例如:--id.attrsimporter=true:ecert,email=user1@gmail.com

以下表格包含用户注册期间使用的默认属性:

属性名称命令行参数属性值
hf.EnrollmentID(automatic)身份的注册 ID
hf.Typeid.type身份的类型
hf.Affiliationid.affiliation身份的从属关系

如果在 ecert 中需要任何先前的属性,则必须首先在用户注册命令中定义它们。例如,以下命令注册user1,其属性为hf.Affiliation=ImporterOrgMSP,该属性将默认复制到 ecert 中:

fabric-ca-client register --id.name user1 --id.secret pwd1 --id.type user --id.affiliation ImporterOrgMSP --id.attrs 'importer=true:ecert,hf.Affiliation=ImporterOrgMSP:ecert'

注册用户

在这里,我们将注册用户并创建 ecert。enrollment.attrs定义了从用户注册中复制到 ecert 的属性。后缀 opt 定义了从注册中复制的这些属性中的哪些是可选的。如果一个或多个非可选属性在用户注册时未定义,则注册将失败。以下命令将注册一个带有属性importer的用户:

fabric-ca-client enroll -u http://user1:pwd1@localhost:7054 --enrollment.attrs "importer,email:opt"

在链码中检索用户身份和属性

在此步骤中,我们将在执行链码期间检索用户的身份。链码可用的 ABAC 功能由客户端身份链码CID)库提供。

提交给链码的每个交易建议都携带着发起者的 ecert – 提交交易的用户。链码通过导入 CID 库并调用带有参数ChaincodeStubInterface的库函数来访问 ecert,即在InitInvoke方法中都收到的参数stub

链码可以使用证书来提取有关调用者的信息,包括:

  • 调用者的 ID

  • 发出调用者证书的**成员服务提供商(MSP)**的唯一 ID

  • 证书的标准属性,如其域名、电子邮件等

  • 存储在证书中与客户端身份相关的 ecert 属性

CID 库提供的函数如下所示:

// Returns the ID associated with the invoking identity. 
// This ID is unique within the MSP (Fabric CA) which issued the identity, however, it is not guaranteed to be unique across all MSPs of the network. 
func GetID() (string, error) 

// Returns the unique ID of the MSP associated with the identity that submitted the transaction. 
// The combination of the MSPID and of the identity ID are guaranteed to be unique across the network. 
func GetMSPID() (string, error) 

// Returns the value of the ecert attribute named `attrName`. 
// If the ecert has the attribute, the `found` returns true and the `value` returns the value of the attribute. 
// If the ecert does not have the attribute, `found` returns false and `value` returns empty string. 
func GetAttributeValue(attrName string) (value string, found bool, err error) 

// The function verifies that the ecert has the attribute named `attrName` and that the attribute value equals to `attrValue`. 
// The function returns nil if there is a match, else, it returns error. 
func AssertAttributeValue(attrName, attrValue string) error 

// Returns the X509 identity certificate. 
// The certificate is an instance of a type Certificate from the library "crypto/x509". 
func GetX509Certificate() (*x509.Certificate, error)  

在以下的代码块中,我们定义了一个名为getTxCreatorInfo的函数,该函数获取调用者的基本身份信息。首先,我们必须导入 CID 和 x509 库,如第 3 和第 4 行所示。第 13 行检索到唯一的 MSPID,第 19 行获取了 X509 证书。然后在第 24 行,我们检索证书的CommonName,其中包含网络中 Fabric CA 的唯一字符串。这两个属性由该函数返回,并在后续的访问控制验证中使用,如以下片段所示:

import ( 
   "fmt" 
   "github.com/hyperledger/fabric/core/chaincode/shim" 
   "github.com/hyperledger/fabric/core/chaincode/lib/cid" 
   "crypto/x509" 
) 

func getTxCreatorInfo(stub shim.ChaincodeStubInterface) (string, string, error) { 
   var mspid string 
   var err error 
   var cert *x509.Certificate 

   mspid, err = cid.GetMSPID(stub) 
   if err != nil { 
         fmt.Printf("Error getting MSP identity: %sn", err.Error()) 
         return "", "", err 
   } 

   cert, err = cid.GetX509Certificate(stub) 
   if err != nil { 
         fmt.Printf("Error getting client certificate: %sn", err.Error()) 
         return "", "", err 
   } 

   return mspid, cert.Issuer.CommonName, nil 
}

现在,我们需要在链码中定义和实现简单的访问控制策略。链码的每个函数只能由特定组织的成员调用;因此,每个链码函数都将验证调用者是否是所需组织的成员。例如,函数requestTrade只能由Importer组织的成员调用。在下面的代码片段中,函数authenticateImporterOrg验证调用者是否是ImporterOrgMSP的成员。然后,将从requestTrade函数调用此函数以执行访问控制。

func authenticateExportingEntityOrg(mspID string, certCN string) bool {
    return (mspID == "ExportingEntityOrgMSP") && (certCN == "ca.exportingentityorg.trade.com")
}
func authenticateExporterOrg(mspID string, certCN string) bool {
return (mspID == "ExporterOrgMSP") && (certCN == "ca.exporterorg.trade.com")
}
func authenticateImporterOrg(mspID string, certCN string) bool {
    return (mspID == "ImporterOrgMSP") && (certCN == "ca.importerorg.trade.com")
}
func authenticateCarrierOrg(mspID string, certCN string) bool {
    return (mspID == "CarrierOrgMSP") && (certCN == "ca.carrierorg.trade.com")
}
func authenticateRegulatorOrg(mspID string, certCN string) bool {
    return (mspID == "RegulatorOrgMSP") && (certCN == "ca.regulatororg.trade.com")
}

下面的代码片段显示了访问控制验证的调用,该验证仅授予ImporterOrgMSP成员访问权限。该函数使用从getTxCreatorInfo函数获取的参数进行调用。

creatorOrg, creatorCertIssuer, err = getTxCreatorInfo(stub)
if !authenticateImporterOrg(creatorOrg, creatorCertIssuer) {
    return shim.Error("Caller not a member of Importer Org. Access denied.")
}

现在,我们需要将身份验证函数放入一个单独的文件accessControlUtils.go中,该文件位于与主tradeWorkflow.go文件相同的目录中。此文件将在编译期间自动导入到主chaincode文件中,因此我们可以引用其中定义的函数。

实现链码函数

到此为止,我们现在拥有链码的基本构建模块。我们有Init方法,用于初始化链码,以及Invoke方法,用于接收来自客户端和访问控制机制的请求。现在,我们需要定义链码的功能。

根据我们的场景,以下表格总结了记录和检索数据以及提供智能合约业务逻辑所需的函数列表。这些表格还定义了组织成员的访问控制定义,以便调用相应的函数。

以下表格说明了链码修改函数,即如何在分类账上记录交易:

函数名称调用权限描述
requestTrade进口商请求贸易协议
acceptTrade出口商接受贸易协议
requestLC进口商请求信用证
issueLC进口商发行信用证
acceptLC出口商接受信用证
requestEL出口商请求出口许可证
issueEL监管机构发行出口许可证
prepareShipment出口商准备装运
acceptShipmentAndIssueBL承运人接受装运并发行提单
requestPayment出口商请求支付
makePayment进口商进行支付
updateShipmentLocation承运人更新装运位置

以下表格说明了链码查询函数,即从分类账中检索数据所需的函数:

函数名称调用权限描述
getTradeStatus出口商/出口实体/进口商获取贸易协议的当前状态
getLCStatus出口商/出口实体/进口商获取信用证的当前状态
getELStatus出口实体/监管机构获取出口许可证的当前状态
getShipmentLocation出口商/出口实体/进口商/承运人获取货物当前位置
getBillOfLading出口商/出口实体/进口商获取提货单
getAccountBalance出口商/出口实体/进口商获取给定参与者的当前账户余额

定义链码资产

现在我们要定义资产的结构,这些资产将记录在账本上。在 Go 语言中,资产被定义为具有属性名和类型列表的结构类型。定义还需要包含 JSON 属性名,这些属性名将用于将资产序列化为 JSON 对象。在下面的片段中,您将看到我们应用程序中四个资产的定义。请注意,结构体的属性可以封装其他结构体,从而允许创建多级树。

type TradeAgreement struct { 
   Amount                    int               `json:"amount"` 
   DescriptionOfGoods        string            `json:"descriptionOfGoods"` 
   Status                    string            `json:"status"` 
   Payment                   int               `json:"payment"` 
} 

type LetterOfCredit struct { 
   Id                        string            `json:"id"` 
   ExpirationDate            string            `json:"expirationDate"` 
   Beneficiary               string            `json:"beneficiary"` 
   Amount                    int               `json:"amount"` 
   Documents                 []string          `json:"documents"` 
   Status                    string            `json:"status"` 
} 

type ExportLicense struct { 
   Id                        string            `json:"id"` 
   ExpirationDate            string            `json:"expirationDate"` 
   Exporter                  string            `json:"exporter"` 
   Carrier                   string            `json:"carrier"` 
   DescriptionOfGoods        string            `json:"descriptionOfGoods"` 
   Approver                  string            `json:"approver"` 
   Status                    string            `json:"status"` 
} 

type BillOfLading struct { 
   Id                        string            `json:"id"` 
   ExpirationDate            string            `json:"expirationDate"` 
   Exporter                  string            `json:"exporter"` 
   Carrier                   string            `json:"carrier"` 
   DescriptionOfGoods        string            `json:"descriptionOfGoods"` 
   Amount                    int               `json:"amount"` 
   Beneficiary               string            `json:"beneficiary"` 
   SourcePort                string            `json:"sourcePort"` 
   DestinationPort           string            `json:"destinationPort"` 
}  

编写链码函数

在本节中,我们将实现之前查看过的链码函数。为了实现链码函数,我们将使用三个 SHIM API 函数,这些函数将从 Worldstate 中读取资产并记录更改。正如我们已经学到的那样,这些函数的读取和写入分别记录到ReadSetWriteSet中,而这些更改并不会立即影响账本的状态。只有在交易通过验证并被提交到账本之后,更改才会生效。

以下片段显示了一系列资产 API 函数:

// Returns the value of the `key` from the Worldstate. 
// If the key does not exist in the Worldstate the function returns (nil, nil). 
// The function does not read data from the WriteSet and hence uncommitted values modified by PutState are not returned. 
func GetState(key string) ([]byte, error) 

// Records the specified `key` and `value` into the WriteSet. 
// The function does not affect the ledger until the transaction is committed into the ledger. 
func PutState(key string, value []byte) error 

// Marks the the specified `key` as deleted in the WriteSet. 
// The key will be marked as deleted and removed from Worldstate once the transaction is committed into the ledger. 
func DelState(key string) error

创建资产

现在我们可以实现我们的第一个链码函数了,接下来我们将实现一个requestTrade函数,它将创建一个新的交易协议,状态为REQUESTED,然后记录该协议到账本上。

函数的实现如下所示。正如您将看到的,第 9 行我们验证调用者是否是ImporterOrg的成员,并且具有调用该函数的权限。从第 13 行到第 21 行,我们验证并提取参数。在第 23 行,我们创建一个以接收到的参数初始化的新的TradeAgreement实例。正如我们之前学到的,账本以字节数组的形式存储值。因此,在第 24 行我们将TradeAgreement序列化为 JSON 并转换为字节数组。在第 32 行,我们创建一个唯一的键,我们将存储TradeAgreement。最后,在第 37 行,我们使用键和序列化的TradeAgreement以及函数PutState将值存储到WriteSet中。

以下片段说明了requestTrade函数:

func (t *TradeWorkflowChaincode) requestTrade(stub shim.ChaincodeStubInterface, creatorOrg string, creatorCertIssuer string, args []string) pb.Response { 
   var tradeKey string 
   var tradeAgreement *TradeAgreement 
   var tradeAgreementBytes []byte 
   var amount int 
   var err error 

   // Access control: Only an Importer Org member can invoke this transaction 
   if !t.testMode && !authenticateImporterOrg(creatorOrg, creatorCertIssuer) { 
         return shim.Error("Caller not a member of Importer Org. Access denied.") 
   } 

   if len(args) != 3 { 
         err = errors.New(fmt.Sprintf("Incorrect number of arguments. Expecting 3: {ID, Amount, Description of Goods}. Found %d", len(args))) 
         return shim.Error(err.Error()) 
   } 

   amount, err = strconv.Atoi(string(args[1])) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 

   tradeAgreement = &TradeAgreement{amount, args[2], REQUESTED, 0} 
   tradeAgreementBytes, err = json.Marshal(tradeAgreement) 
   if err != nil { 
         return shim.Error("Error marshaling trade agreement structure") 
   } 

   // Write the state to the ledger 
   tradeKey, err = getTradeKey(stub, args[0]) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 
   err = stub.PutState(tradeKey, tradeAgreementBytes) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 
   fmt.Printf("Trade %s request recorded", args[0]) 

   return shim.Success(nil) 
}  

读取和修改资产

当我们实现了创建交易协议的函数之后,我们需要实现一个函数来接受交易协议。该函数将检索协议,将其状态修改为ACCEPTED,并将其放回到账本上。

此函数的实现如下所示。在代码中,我们构造了我们想要检索的贸易协议的唯一的复合键。在第 22 行,我们使用GetState函数检索值。在第 33 行,我们将字节数组反序列化为TradeAgreement结构的实例。在第 41 行,我们修改状态,使其为ACCEPTED;最后,在第 47 行,我们将更新后的值存储在分类账上,如下所示:

func (t *TradeWorkflowChaincode) acceptTrade(stub shim.ChaincodeStubInterface, creatorOrg string, creatorCertIssuer string, args []string) pb.Response { 
   var tradeKey string 
   var tradeAgreement *TradeAgreement 
   var tradeAgreementBytes []byte 
   var err error 

   // Access control: Only an Exporting Entity Org member can invoke this transaction 
   if !t.testMode && !authenticateExportingEntityOrg(creatorOrg, creatorCertIssuer) { 
         return shim.Error("Caller not a member of Exporting Entity Org. Access denied.") 
   } 

   if len(args) != 1 { 
         err = errors.New(fmt.Sprintf("Incorrect number of arguments. Expecting 1: {ID}. Found %d", len(args))) 
         return shim.Error(err.Error()) 
   } 

   // Get the state from the ledger 
   tradeKey, err = getTradeKey(stub, args[0]) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 
   tradeAgreementBytes, err = stub.GetState(tradeKey) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 

   if len(tradeAgreementBytes) == 0 { 
         err = errors.New(fmt.Sprintf("No record found for trade ID %s", args[0])) 
         return shim.Error(err.Error()) 
   } 

   // Unmarshal the JSON 
   err = json.Unmarshal(tradeAgreementBytes, &tradeAgreement) 
   if err != nil { 
         return shim.Error(err.Error()) 
   } 

   if tradeAgreement.Status == ACCEPTED { 
         fmt.Printf("Trade %s already accepted", args[0]) 
   } else { 
         tradeAgreement.Status = ACCEPTED 
         tradeAgreementBytes, err = json.Marshal(tradeAgreement) 
         if err != nil { 
               return shim.Error("Error marshaling trade agreement structure") 
         } 
         // Write the state to the ledger 
         err = stub.PutState(tradeKey, tradeAgreementBytes) 
         if err != nil { 
               return shim.Error(err.Error()) 
         } 
   } 
   fmt.Printf("Trade %s acceptance recordedn", args[0]) 

   return shim.Success(nil) 
}  

主函数

最后但并非最不重要的是,我们将添加main函数:Go 程序的初始点。当链码的实例部署在 peer 上时,会执行main函数以启动链码。

在下面片段的第 2 行中,实例化了链码。 函数shim.Start在第 4 行启动了链码,并向 peer 注册了链码,如下所示:

func main() { 
   twc := new(TradeWorkflowChaincode) 
   twc.testMode = false 
   err := shim.Start(twc) 
   if err != nil { 
         fmt.Printf("Error starting Trade Workflow chaincode: %s", err) 
   } 
} 

测试链码

现在我们可以为我们的链码函数编写单元测试,我们将使用内置的自动化 Go 测试框架。有关更多信息和文档,请访问 Go 的官方网站:golang.org/pkg/testing/

框架自动寻找并执行以下签名的函数:

 func TestFname(*testing.T)

函数名Fname是一个任意的名称,必须以大写字母开头。

请注意,包含单元测试的测试套件文件必须以后缀_test.go结束;因此,我们的测试套件文件将被命名为tradeWorkflow_test.go,并放置在与我们的chaincode文件相同的目录中。test函数的第一个参数是类型为T的,它提供了用于管理测试状态并支持格式化测试日志的函数。测试的输出被写入标准输出,可以在终端中检查。

SHIM 模拟

SHIM 包提供了一个全面的模拟模型,可用于测试链码。在我们的单元测试中,我们将使用MockStub类型,它为单元测试链码提供了ChaincodeStubInterface的实现。

测试Init方法

首先,我们需要定义一个函数,用于调用Init方法。该函数将接收对MockStub的引用,以及一个传递给Init方法的参数数组。在以下代码的第 2 行,使用接收到的参数调用链码函数Init,然后在第 3 行进行验证。

以下代码片段演示了Init方法的调用:

 func checkInit(t *testing.T, stub *shim.MockStub, args [][]byte) { 
   res := stub.MockInit("1", args) 
   if res.Status != shim.OK { 
         fmt.Println("Init failed", string(res.Message)) 
         t.FailNow() 
   } 
} 

我们将定义一个函数,用于准备Init函数参数的默认值数组,如下所示:

func getInitArguments() [][]byte { 
   return [][]byte{[]byte("init"), 
               []byte("LumberInc"), 
               []byte("LumberBank"), 
               []byte("100000"), 
               []byte("WoodenToys"), 
               []byte("ToyBank"), 
               []byte("200000"),
               []byte("UniversalFreight"), 
               []byte("ForestryDepartment")} 
} 

现在我们将定义Init函数的测试,如下所示。测试首先创建链码的一个实例,然后将模式设置为测试,最后为链码创建一个新的MockStub。在第 7 行,调用checkInit函数并执行Init函数。最后,从第 9 行开始,我们将验证分类账的状态,如下所示:

func TestTradeWorkflow_Init(t *testing.T) { 
   scc := new(TradeWorkflowChaincode) 
   scc.testMode = true 
   stub := shim.NewMockStub("Trade Workflow", scc) 

   // Init 
   checkInit(t, stub, getInitArguments()) 

   checkState(t, stub, "Exporter", EXPORTER) 
   checkState(t, stub, "ExportersBank", EXPBANK) 
   checkState(t, stub, "ExportersAccountBalance", strconv.Itoa(EXPBALANCE)) 
   checkState(t, stub, "Importer", IMPORTER) 
   checkState(t, stub, "ImportersBank", IMPBANK) 
   checkState(t, stub, "ImportersAccountBalance", strconv.Itoa(IMPBALANCE)) 
   checkState(t, stub, "Carrier", CARRIER) 
   checkState(t, stub, "RegulatoryAuthority", REGAUTH) 
}

接下来,我们将通过checkState函数验证每个键的状态是否符合预期,如以下代码块所示:

func checkState(t *testing.T, stub *shim.MockStub, name string, value string) { 
  bytes := stub.State[name] 
  if bytes == nil { 
    fmt.Println("State", name, "failed to get value") 
    t.FailNow() 
  } 
  if string(bytes) != value {
    fmt.Println("State value", name, "was", string(bytes), "and not", value, "as expected")
    t.FailNow()
  }
} 

测试调用方法

现在是定义Invoke函数的测试的时候了。在以下代码块的第 7 行,调用checkInit来初始化总账,然后在第 13 行调用checkInvoke,调用requestTrade函数。requestTrade函数创建一个新的贸易资产并将其存储在总账上。在第 15 和 16 行创建并序列化一个新的TradeAgreement,然后在第 17 行计算一个新的复合键。最后,在第 18 行,验证键的状态是否与序列化的值相匹配。

此外,正如前面所述,我们的链码包含一系列函数,这些函数一起定义了贸易工作流程。我们将在测试中将这些函数的调用链接成一个序列,以验证整个工作流程。整个函数的代码可以在位于chaincode文件夹中的测试文件中找到。

func TestTradeWorkflow_Agreement(t *testing.T) { 
   scc := new(TradeWorkflowChaincode) 
   scc.testMode = true 
   stub := shim.NewMockStub("Trade Workflow", scc) 

   // Init 
   checkInit(t, stub, getInitArguments()) 

   // Invoke 'requestTrade' 
   tradeID := "2ks89j9" 
   amount := 50000 
   descGoods := "Wood for Toys" 
   checkInvoke(t, stub, [][]byte{[]byte("requestTrade"), []byte(tradeID), []byte(strconv.Itoa(amount)), []byte(descGoods)}) 

   tradeAgreement := &TradeAgreement{amount, descGoods, REQUESTED, 0} 
   tradeAgreementBytes, _ := json.Marshal(tradeAgreement) 
   tradeKey, _ := stub.CreateCompositeKey("Trade", []string{tradeID}) 
   checkState(t, stub, tradeKey, string(tradeAgreementBytes)) 
   ... 
}

下面的代码段显示了checkInvoke函数。

func checkInvoke(t *testing.T, stub *shim.MockStub, args [][]byte) { 
   res := stub.MockInvoke("1", args) 
   if res.Status != shim.OK { 
         fmt.Println("Invoke", args, "failed", string(res.Message)) 
         t.FailNow() 
   } 
}

运行测试

现在我们准备好运行我们的测试了!go test命令将执行在tradeWorkflow_test.go文件中找到的所有测试。该文件包含一系列测试,验证了我们工作流中定义的函数。

现在让我们使用以下命令在终端中运行测试:

$ cd $GOPATH/src/trade-finance-logistics/chaincode/src/github.com/trade_workflow_v1 
$ go test 

前面的命令应该生成以下输出:

Initializing Trade Workflow 
Exporter: LumberInc 
Exporter's Bank: LumberBank 
Exporter's Account Balance: 100000 
Importer: WoodenToys 
Importer's Bank: ToyBank 
Importer's Account Balance: 200000 
Carrier: UniversalFreight 
Regulatory Authority: ForestryDepartment 
... 
Amount paid thus far for trade 2ks89j9 = 25000; total required = 50000 
Payment request for trade 2ks89j9 recorded 
TradeWorkflow Invoke 
TradeWorkflow Invoke 
Query Response:{"Balance":"150000"} 
TradeWorkflow Invoke 
Query Response:{"Balance":"150000"} 
PASS 
ok       trade-finance-logistics/chaincode/src/github.com/trade_workflow_v1      0.036s 

链码设计主题

复合键

我们经常需要在总帐上存储一个类型的多个实例,比如多个贸易协议、信用证等等。在这种情况下,这些实例的键通常将由多个属性的组合构造而成,例如"Trade" + ID, yielding ["Trade1","Trade2", ...]。实例的键可以在代码中自定义,或者在 SHIM 中提供 API 函数来构造实例的复合键(换句话说,基于几个属性的唯一键)。这些函数简化了复合键的构造。复合键可以像普通字符串键一样使用PutState()GetState()函数来记录和检索值。

以下代码段显示了一系列创建和使用复合键的函数:

// The function creates a key by combining the attributes into a single string. 
// The arguments must be valid utf8 strings and must not contain U+0000 (nil byte) and U+10FFFF charactres. 
func CreateCompositeKey(objectType string, attributes []string) (string, error) 

// The function splits the compositeKey into attributes from which the key was formed. 
// This function is useful for extracting attributes from keys returned by range queries. 
func SplitCompositeKey(compositeKey string) (string, []string, error) 

在下面的代码段中,我们可以看到一个名为getTradeKey的函数,它通过将关键字Trade与贸易的 ID 组合构造了一个唯一的复合键:

func getTradeKey(stub shim.ChaincodeStubInterface, tradeID string) (string, error) { 
   tradeKey, err := stub.CreateCompositeKey("Trade", []string{tradeID}) 
   if err != nil { 
         return "", err 
   } else { 
         return tradeKey, nil 
   } 
}

在更复杂的情况下,键可以由多个属性构造。复合键还允许您根据键的组件在范围查询中搜索资产。我们将在接下来的部分更详细地探讨搜索。

范围查询

除了使用唯一键检索资产之外,SHIM 还提供 API 函数来根据范围条件检索一系列资产。此外,可以对复合键进行建模,以便查询多个键的组件。

范围函数返回与查询条件匹配的一组键的迭代器(StateQueryIteratorInterface)。 返回的键按字典顺序排列。 迭代器必须通过调用Close()函数关闭。 此外,当复合键具有多个属性时,范围查询函数GetStateByPartialCompositeKey()可用于搜索匹配部分属性的键。

例如,由TradeIdPaymentId组成的支付密钥可以在与特定TradeId相关联的所有支付中进行搜索,如下片段所示:

 // Returns an iterator over all keys between the startKey (inclusive) and endKey (exclusive). 
// To query from start or end of the range, the startKey and endKey can be an empty. 
func GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) 

// Returns an iterator over all composite keys whose prefix matches the given partial composite key. 
// Same rules as for arguments of CreateCompositeKey function apply. 
func GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error) 

我们还可以使用以下查询搜索 ID 范围在 1-100 之间的所有贸易协议:

startKey, err = getTradeKey(stub, "1") 
endKey, err = getTradeKey(stub, "100") 

keysIterator, err := stub.GetStateByRange(startKey, endKey) 
if err != nil { 
    return shim.Error(fmt.Printf("Error accessing state: %s", err)) 
} 

defer keysIterator.Close() 

var keys []string 
for keysIterator.HasNext() { 
    key, _, err := keysIterator.Next() 
    if err != nil { 
        return shim.Error(fmt.Printf("keys operation failed. Error accessing state: %s", err)) 
    } 
    keys = append(keys, key) 
}

状态查询和 CouchDB

默认情况下,Fabric 使用 LevelDB 作为 Worldstate 的存储。 Fabric 还提供了配置对等方将 Worldstate 存储在 CouchDB 中的选项。 当资产以 JSON 文档的形式存储时,CouchDB 允许您根据资产状态执行复杂的查询。

查询采用本机 CouchDB 声明性 JSON 查询语法格式化。 此语法的当前版本可在以下链接找到:docs.couchdb.org/en/2.1.1/api/database/find.html.

Fabric 将查询转发到 CouchDB 并返回一个迭代器(StateQueryIteratorInterface()),该迭代器可用于迭代结果集。 基于状态的查询函数的声明如下所示:

func GetQueryResult(query string) (StateQueryIteratorInterface, error)

在下面的代码片段中,我们可以看到一个基于状态的查询,用于所有状态为ACCEPTED且收到的付款超过 1000 的贸易协议。 然后执行查询,并将找到的文档写入终端,如下所示:

// CouchDB query definition
queryString :=
`{
    "selector": {
            "status": "ACCEPTED"
            "payment": {
                    "$gt": 1000
            }
    }
}`

fmt.Printf("queryString:\n%s\n", queryString)

// Invoke query
resultsIterator, err := stub.GetQueryResult(queryString)
if err != nil {
    return nil, err
}
defer resultsIterator.Close()

var buffer bytes.Buffer
buffer.WriteString("[")

// Iterate through all returned assets
bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
    queryResponse, err := resultsIterator.Next()
    if err != nil {
        return nil, err
    }
    if bArrayMemberAlreadyWritten == true {
        buffer.WriteString(",")
    }
    buffer.WriteString("{\"Key\":")
    buffer.WriteString("\"")
    buffer.WriteString(queryResponse.Key)
    buffer.WriteString("\"")

    buffer.WriteString(", \"Record\":")
    buffer.WriteString(string(queryResponse.Value))
    buffer.WriteString("}")
    bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")

fmt.Printf("queryResult:\n%s\n", buffer.String())

请注意,与键的查询不同,对状态的查询不会记录到交易的ReadSet中。 因此,交易的验证实际上无法验证在执行和提交交易之间 Worldstate 的更改。 因此,链码设计必须考虑到这一点; 如果查询基于预期的调用序列,那么无效的交易可能会出现。

索引

在大型数据集上执行查询是一项计算复杂的任务。 Fabric 提供了在 CouchDB 托管的 Worldstate 上定义索引以提高效率的机制。 请注意,索引也是查询中排序操作所必需的。

索引在一个扩展名为*.json的单独文件中以 JSON 格式定义。 格式的完整定义可在以下链接找到:docs.couchdb.org/en/2.1.1/api/database/find.html#db-index

以下代码片段说明了一个与我们之前查看的贸易协议查询匹配的索引:

 { 
  "index": { 
    "fields": [ 
      "status", 
      "payment" 
    ] 
  }, 
  "name": "index_sp", 
  "type": "json" 
}  

在这里,索引文件被放置到文件夹/META-INF/statedb/couchdb/indexes中。在编译期间,索引与链码一起打包。在对等体上安装和实例化链码后,索引会自动部署到世界状态并用于查询。

读集(ReadSet)和写集(WriteSet)

当接收到来自客户端的事务调用消息时,背书节点会执行一个事务。执行会在对等节点的世界状态上下文中调用链码,并将其数据的所有读取和写入记录到ReadSetWriteSet中。

交易的WriteSet包含了在链码执行期间被修改的键值对列表。当修改键的值时(即记录新键值对或使用新值更新现有键),WriteSet会包含更新后的键值对。

当键被删除时,WriteSet会包含具有标记键为已删除的属性的键。如果在链码执行期间多次修改单个键,则WriteSet会包含最新修改的值。

交易的ReadSet包含了在链码执行期间访问的键及其版本的列表。键的版本号由区块号和区块内事务号的组合派生而来。这种设计使得数据的高效搜索和处理成为可能。交易的另一部分包含了关于范围查询及其结果的信息。请记住,当链码读取键的值时,会返回账本中最新提交的值。

如果链码执行期间引入的修改被存储在WriteSet中,当链码读取在执行期间被修改的键时,会返回已提交而非修改后的值。因此,如果后续需要修改后的值,则必须实现链码以保留并使用正确的值。

一个交易的ReadSetWriteSet的示例如下:

{
  "rwset": {
    "reads": [
      {
        "key": "key1",
        "version": {
          "block_num": {
            "low": 9546,
            "high": 0,
            "unsigned": true
          },
          "tx_num": {
            "low": 0,
            "high": 0,
            "unsigned": true
          }
        }
      }
    ],
    "range_queries_info": [],
    "writes": [
      {
        "key": "key1",
        "is_delete": false,
        "value": "value1"
      },
      {
        "key": "key2",
        "is_delete": true
      }
    ]
  }
}

多版本并发控制

Fabric 使用多版本并发控制MVCC)机制来确保账本的一致性并防止双重支付。双重支付攻击旨在通过引入使用或多次修改同一资源的事务来利用系统中的缺陷,比如在加密货币网络中多次花费同一枚硬币。键碰撞是另一种可能发生的问题类型,它可能会在并行客户端提交的事务处理中尝试同时修改相同的键/值对。

此外,由于 Fabric 的去中心化架构,事务执行顺序可以在不同的 Fabric 组件(包括背书者,排序者和提交者)上有不同的排序和提交方式,从而在交易计算和提交之间引入延迟,其中可能发生键冲突。去中心化还使网络容易受到客户端故意或无意地修改交易顺序的潜在问题和攻击的影响。

为确保一致性,像数据库这样的计算机系统通常使用锁定机制。然而,在 Fabric 中无法使用这种集中式方法。同时,也值得注意的是,锁定有时可能会导致性能下降。

为了应对这一点,Fabric 使用了存储在总账簿上的键的版本系统。版本系统的目标是确保交易按照不引入不一致性的顺序被排序和提交到总账簿中。当在提交对等方收到一个块时,会验证块中的每笔交易。该算法会检查ReadSet中的键及其版本;如果ReadSet中每个键的版本与世界状态中相同键的版本,或同一块中之前的交易的版本相匹配,则交易被视为有效。换句话说,该算法验证执行交易期间从世界状态读取的任何数据是否已发生更改。

如果一个事务包含范围查询,这些查询也将得到验证。对于每个范围查询,算法会检查在链码执行期间执行查询的结果是否与之前完全相同,或者是否发生了任何修改。

未通过此验证的交易将在总账簿中标记为无效,并且它们所引入的更改不会映射到世界状态中。需要注意的是,由于总账簿是不可修改的,这些交易会保留在总账簿上。

如果交易通过了验证,WriteSet将被映射到世界状态。交易修改的每个键都会在世界状态中设置为WriteSet中指定的新值,并且该键在世界状态中的版本会设置为从交易中导出的版本。通过这种方式,任何重复支出等不一致性将被防止。同时,在可能发生键冲突的情况下,链码设计必须考虑 MVCC 的行为。针对键冲突和 MVCC,存在多种公认的解决策略,如分割资产、使用多个键、事务排队等。

日志输出

日志记录是系统代码的重要组成部分,它使得可以分析和检测运行时问题。

Fabric 中的日志记录是基于标准的 Go 日志包github.com/op/go-logging。日志机制提供基于严重性的日志控制和消息的漂亮打印装饰。日志级别按严重性递减的顺序定义,如下所示:

CRITICAL | ERROR | WARNING | NOTICE | INFO | DEBUG 

所有组件生成的日志消息都会组合并写入标准错误文件(stderr)。可以通过对对等体和模块的配置以及 chaincode 的代码来控制日志记录。

配置

对等体日志的默认配置设置为级别 INFO,但可以通过以下方式控制此级别:

  1. 命令行选项日志级别。此选项覆盖默认配置,如下所示:
peer node start --logging-level=error  

请注意,通过命令行选项可以配置任何模块或 chaincode,如下所示:

 peer node start --logging-level=chaincode=error:main=info
  1. 默认的日志级别也可以通过environment变量CORE_LOGGING_LEVEL来定义,默认配置如下所示:
peer0.org1.example.com:
    environment:
        - CORE_LOGGING_LEVEL=error
  1. core.yml文件中的一个配置属性,定义了网络的配置,也可以与以下代码一起使用:
logging:
    level: info
  1. core.yml 文件还允许您配置特定模块的日志级别,例如chaincode模块或消息格式,如下节选所示:
 chaincode: 
   logging: 
         level:  error 
         shim:   warning  

关于各种配置选项的详细信息包含在core.yml文件的注释中。

日志 API

SHIM 包提供了 API,供 chaincode 创建和管理日志对象。这些对象生成的日志与对等体日志集成。

chaincode 可以创建和使用任意数量的日志对象。每个日志对象必须有一个唯一的名称,用于在输出中添加日志记录的前缀并区分不同日志对象和 SHIM 的记录。(请记住,日志对象名称 SHIM API 是保留的,不应在 chaincode 中使用。)每个日志对象都设置了一个日志严重级别,在该级别下记录日志将被发送到输出中。具有严重级别CRITICAL的日志记录始终出现在输出中。以下节选列出了在 chaincode 中创建和管理日志对象的 API 函数。

// Creates a new logging object. 
func NewLogger(name string) *ChaincodeLogger 

// Converts a case-insensitive string representing a logging level into an element of LoggingLevel enumeration type. 
// This function is used to convert constants of standard GO logging levels (i.e. CRITICAL, ERROR, WARNING, NOTICE, INFO or DEBUG) into the shim's enumeration LoggingLevel type (i.e. LogDebug, LogInfo, LogNotice, LogWarning, LogError, LogCritical). 
func LogLevel(levelString string) (LoggingLevel, error) 

// Sets the logging level of the logging object. 
func (c *ChaincodeLogger) SetLevel(level LoggingLevel) 

// Returns true if the logging object will generate logs at the given level. 
func (c *ChaincodeLogger) IsEnabledFor(level LoggingLevel) bool 

日志对象ChaincodeLogger提供了每个严重级别的日志记录函数。以下是ChaincodeLogger的函数列表。

func (c *ChaincodeLogger) Debug(args ...interface{}) 
func (c *ChaincodeLogger) Debugf(format string, args ...interface{}) 
func (c *ChaincodeLogger) Info(args ...interface{}) 
func (c *ChaincodeLogger) Infof(format string, args ...interface{}) 
func (c *ChaincodeLogger) Notice(args ...interface{}) 
func (c *ChaincodeLogger) Noticef(format string, args ...interface{}) 
func (c *ChaincodeLogger) Warning(args ...interface{}) 
func (c *ChaincodeLogger) Warningf(format string, args ...interface{}) 
func (c *ChaincodeLogger) Error(args ...interface{}) 
func (c *ChaincodeLogger) Errorf(format string, args ...interface{}) 
func (c *ChaincodeLogger) Critical(args ...interface{}) 
func (c *ChaincodeLogger) Criticalf(format string, args ...interface{}) 

记录的默认格式由 SHIM 的配置定义,该配置在输入参数的打印表示之间添加空格。对于每个严重级别,日志对象还提供了一个带有后缀f的附加函数,这些函数允许您使用参数format来控制输出的格式。

由日志对象生成的输出模板如下:

[timestamp] [logger name] [severity level] printed arguments 

所有日志对象和 SHIM 的输出都会合并并发送到标准错误(stderr)中。

以下代码块说明了如何创建和使用日志对象的示例:

var logger = shim.NewLogger("tradeWorkflow") 
logger.SetLevel(shim.LogDebug) 

_, args := stub.GetFunctionAndParameters() 
logger.Debugf("Function: %s(%s)", "requestTrade", strings.Join(args, ",")) 

if !authenticateImporterOrg(creatorOrg, creatorCertIssuer) { 
   logger.Info("Caller not a member of Importer Org. Access denied:", creatorOrg, creatorCertIssuer) 
} 

SHIM 日志级别

链码还可以通过使用 API 函数 SetLoggingLevel 直接控制其 SHIM 的日志记录严重级别,如下所示:

logLevel, _ := shim.LogLevel(os.Getenv("TW_SHIM_LOGGING_LEVEL"))
shim.SetLoggingLevel(logLevel)

Stdout 和 stderr

除了由 SHIM API 提供并与对等节点集成的日志记录机制外,在开发阶段,链码可以使用标准输出文件。链码作为一个独立的进程执行,因此可以使用标准输出(stdout)和标准错误(stderr)文件来使用标准的 Go 打印函数记录输出(例如,fmt.Printf(...)os.Stdout)。默认情况下,在 Dev 模式下启动链码进程时,标准输出可用。

在生产环境中,当链码进程由对等节点管理时,出于安全原因,标准输出被禁用。当需要时,可以通过设置对等节点的配置变量 CORE_VM_DOCKER_ATTACHSTDOUT 来启用它。然后,链码的输出将与对等节点的输出合并。请注意,这些输出仅应用于调试目的,不应在生产环境中启用。

以下片段说明了其他 SHIM API 函数:

peer0.org1.example.com: 
   environment: 
         - CORE_VM_DOCKER_ATTACHSTDOUT=true 

列表 4.1:在 docker-compose 文件中启用对等节点上链码标准输出文件。

其他 SHIM API 函数

本节我们提供了剩余的适用于链码的 SHIM API 函数的概述。

 // Returns an unique Id of the transaction proposal. 
func GetTxID() string 

// Returns an Id of the channel the transaction proposal was sent to. 
func GetChannelID() string 

// Calls an Invoke function on a specified chaincode, in the context of the current transaction. 
// If the invoked chaincode is on the same channel, the ReadSet and WriteSet will be added into the same transaction. 
// If the invoked chaincode is on a different channel, the invocation can be used only as a query. 
func InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response 

// Returns a list of historical states, timestamps and transactions ids. 
func GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) 

// Returns the identity of the user submitting the transaction proposal. 
func GetCreator() ([]byte, error) 

// Returns a map of fields containing cryptographic material which may be used to implement custom privacy layer in the chaincode. 
func GetTransient() (map[string][]byte, error) 

// Returns data which can be used to enforce a link between application data and the transaction proposal. 
func GetBinding() ([]byte, error) 

// Returns data produced by peer decorators which modified the chaincode input. 
func GetDecorations() map[string][]byte 

// Returns data elements of a transaction proposal. 
func GetSignedProposal() (*pb.SignedProposal, error) 

// Returns a timestamp of the transaction creation by the client. The timestamp is consistent across all endorsers. 
func GetTxTimestamp() (*timestamp.Timestamp, error) 

// Sets an event attached to the transaction proposal response. This event will be be included in the block and ledger. 
func SetEvent(name string, payload []byte) error  

总结

设计和实现一个功能良好的链码是一项复杂的软件工程任务,它需要对 Fabric 架构、API 函数以及 GO 语言的知识,以及对业务需求的正确实现。

在本章中,我们逐步学习了如何启动适用于链码实现和测试的开发模式的区块链网络,以及如何使用 CLI 部署和调用链码。然后我们学习了如何实现我们场景的链码。我们通过 InitInvoke 函数探索了链码如何接收来自客户端的请求,探索了访问控制机制以及开发人员可用于实现链码功能的各种 API。

最后,我们学习了如何测试链码以及如何将日志功能集成到代码中。为了为下一章做好准备,现在应该使用 ./trade.sh down -d true 停止网络。

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值