fabric-Tutorials
Wei Jieyang Lv4

前言

本部分详细介绍了Hyperledger Fabric中的客户端应用程序和智能合约开发的关键功能。对功能的深入了解将帮助您设计和实施高效的解决方案。

智能合约

智能合约是高级编程抽象的一个示例,可以在链码容器中定义智能合约。将链码安装在对等方上并部署到通道后,其中的所有智能合约都可用于您的应用程序。

一个智能合约只能属于一个chaincode,但一个chaincode中可以包含多个智能合约

  • 合同名称在链码文件中必须唯一。某些代码编辑器会在部署之前检测到相同类名的多个定义。如果显式或隐式指定了多个具有相同协定名称的类,则链代码将返回错误。
  • 不指定链码中的合约将默认使用第一个合约
  • 单个链码中存放多个相关合约是有意义的
    • 例如,在不同的货币计价的可能有合同的商业票据EuroPaperContractDollarPaperContractYenPaperContract这可能需要保持在它们所部署的通道相互同步。

链码

链码是用于将代码部署到Hyperledger Fabric区块链网络的通用容器

当写好链码文件后(此例中为 commercialpaper.js),需要经过以下步骤:

  • 打包:peer chaincode package
  • 下载到通道:peer chaincode install
  • 组织管理员批准链码:peer chaincode approveformyorg
  • 当满足制定策略之后,通道上任一节点均可提交此链码:peer chaincode commit

namespace

链码的namespace允许它保持其世界状态与其他链码分离。

  • 具体来说,具有相同链码的智能合约共享对同一世界状态的直接访问,而具有不同链码的智能合约不能直接访问彼此的世界状态。

    • 即Chaincode命名空间提供了不同世界状态之间的隔离

      注意:不能选择链码名称空间。它由Hyperledger Fabric分配,并直接映射到链码的名称

  • 如果智能合约需要访问另一个链码世界状态,则可以通过执行链码到链码的调用来做到这一点。

  • 最后,区块链可以包含与不同世界状态相关的交易。

注意:

将同一个链码部署到不同peer的channel上时,将创建一个完全独立的区块链,并创建两个新的世界状态数据库。但是,peer和chaincode container不会增加,每个都将连接到多个通道。

同链码访问

示例图:

![截屏2021-03-19 下午9.02.21](fabric-Tutorials/截屏2021-03-19 下午9.02.21.png)

  • 应用程序与对等方进行通信,并且对等方将请求路由到适当的链码容器,然后容器可以访问DBMS。该路由由图中所示的对等核心组件完成。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
const euroPaper = network.getContract(papers, euroPaper);
paper1 = euroPaper.submit(issue, PAP11);

const yenPaper = network.getContract(papers, yenPaper);
paper2 = yenPaper.submit(redeem, PAP21);

const euroBond = network.getContract(bonds, euroBond);
bond1 = euroBond.submit(buy, BON31);

const yenBond = network.getContract(bonds, yenBond);
bond2 = yenBond.submit(sell, BON41);
  1. 应用程序
    • 使用 getContract() API访问 papers链码中的 euroPaperyenPaper 合约。
      • 参见交互点1a2a
    • 使用 getContract()。API访问 bonds 链码中的 euroBondyenBond 合约。
      • 参见交互点3a4a
    • 使用 euroPaper 合约将 issue 交易提交到网络以获取商业票据 PAP11。参见相互作用点1a。结果在 world state A 中产生了商业票据 PAP11;相互作用点1b。该操作在交互点1c被捕获为区块链中的交易。
    • 使用 yenPaper 合约将 redeem 交易提交到网络以获取商业票据 PAP21。参见交互点2a。这导致在world state A 中商业票据 PAP21 的状态为赎回中;交互点2b。该操作在交互点2c被捕获为区块链中的交易。
    • 使用 euroBond 合约操作 BON41 进行 buy 交易并提交到网络。参见相互作用点3a。这导致在 world state BBON31 的状态更新,相互作用点3b。该操作在交互点3c被捕获为区块链中的交易。
    • 使用 yenBond 合约操作 BON41 进行 sell 交易并提交到网络。参见相互作用点4a。这导致在 world state BBON41 的状态更新,相互作用点 4b。该操作在交互点4c被捕获为区块链中的交易。
  1. 了解智能合约如何与世界状态交互:
  • euroPaperyenPaper合同可以直接访问 world state A,但不能直接访问 world state BWorld state A 物理地存储在与链码 papers 相对应的数据库管理系统(DBMS)中的数据库中。
  • euroBondyenBond合同可以直接访问 world state B,但不能直接访问 world state AWorld state B 物理地存储在与链码 Bonds 相对应的数据库管理系统(DBMS)中的数据库中。
  1. 了解区块链如何捕获所有世界各州的交易:
  • 互动1C2C对应的交易分别创建和更新商业票据PAP11PAP21分别。这些都包含在 world state A
  • 互动3c4c对应于更新债券 BON31BON41 的交易。这些都包含 world state B 中。
  • 如果 world state Aworld state B 由于某种原因被销毁,则可以通过重新执行区块链中的所有交易来重新创建它们。

跨链码访问

示例图

develop.diagram.51

  1. 链码和智能合约如何通过他们的链码间接访问其他世界状态
    • 该应用程序在 euroPaper 智能合约中提交 issue 来发行 PAP11。参见互动1a
    • 智能合约 euroPaper 中的 issue 交易需要调用 euroBond 智能合约中的 query 交易。参见相互作用点1b
    • 在智能合约 euroBondquery 交易能检索 world state B 的信息。参见相互作用点1c
    • 当控制权返回 issue 交易时,它可以使用响应中的信息对 paper 定价并更新 。world state A 的信息。参见相互作用点1d
    • 发行以Yen计价的商业票据的控制流程是相同的。参见交互点2a2b2c2d

使用invokeChaincode() API在链代码之间传递control。

  • 该API将control从一个链码传递到另一个链码。
  • 如果只 query 被调用的链码世界状态,调用可以在与调用者的链码不同的通道中进行。
  • 如果需要 update 被调用的链码世界状态,调用必须与调用者链码在同一通道中。

尽管在示例中我们仅讨论了查询交易,但是可以调用智能合约来更新被调用的链码的世界状态

命名

在commercial paper中,链码papercontract中只包含了一个名为 org.papernet.commercialpaper 的合约

  • 合约构造

    1
    2
    3
    4
    5
    6
    7
    class CommercialPaperContract extends Contract {
    constructor() {
    // Unique name when multiple contracts per chaincode file
    super('org.papernet.commercialpaper');
    }
    ... ...
    }
    • 如果未指定显式合同名称,则将分配默认名称 - 类的名称。
      • 在我们的示例中,默认合同名称CommercialPaperContract
    • 建议使用显式的DNS样式命名约定,以帮助组织清晰且有意义的名称
      • org.papernet.commercialpaper 表示PaperNet网络已经定义了标准的商业用纸智能合约
  • 合同名称在链码文件中必须唯一。某些代码编辑器会在部署之前检测到相同类名的多个定义。如果显式或隐式指定了多个具有相同协定名称的类,则链代码将返回错误。

使用

将链码安装到对等方并部署到通道后,应用程序即可访问其中的智能合约:

1
2
3
const network = await gateway.getNetwork(`papernet`);
const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
  • CommercialPaperContract 使用名称 org.papernet.commercialpaper 调用父类Contract来构造实例
  • 所以network可以通过此名称定为到 CommercialPaperContract 类
  • contract 对象再调用CommercialPaperContract 类中定义的方法进行交易

交易

Context

  • 允许智能合约在交易调用之间定义和维护用户变量

  • 可以包含任意数量的用户变量,例如 paperList

  • 注意到一个智能合约的初始化流程:(路径:contract/papercontract.js)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class CommercialPaperContext extends Context {
    constructor() {
    super();
    // All papers are held in a list of papers
    this.paperList = new PaperList(this);
    }

    }

    class CommercialPaperContract extends Contract {
    constructor() {
    // Unique namespace when multiple contracts per chaincode file
    super('org.papernet.commercialpaper');
    }
    createContext() {
    return new CommercialPaperContext();
    }
    async issue(ctx, issuer, paperNumber, ...){
    await ctx.paperList.addPaper(...);
    await ctx.stub.putState(...);
    }
    async buy(ctx, issuer, paperNumber, ...){ }
    async redeem(ctx, issuer, paperNumber, ...){ }
    ... ...
    }
    • 构建智能合约时,开发人员可以选择内置的 ContextcreateContext 方法以创建自定义上下文
    • 本例中,CommercialPaperContext专门用于 CommercialPaperContract。观察指定的 context 如何通过 this 关键字将 PaperList 添加到自己的成员中

transaction context还包含两个内置元素,这些元素提供对范围广泛的Fabric功能的访问,从提交交易的客户端应用程序到分类帐访问。

  • **ctx.stub**:用于访问API,这些API从分类账提供各种交易处理操作
    • putState()getState() 访问分类账,getTxID() 以检索当前交易ID。
  • **ctx.clientIdentity**: 用于获取有关提交交易的用户身份的信息。

一个智能合约通过 stubclientIdentity 可以访问一系列的功能:

develop.diagram.41

Stub

API分类:

  1. World state data APIs - 交互点**(1)**
  1. 私有数据APIs - 交互点**(3)**
  1. Transaction APIs - 交互点**(5)**
    • 智能合约使用这些API来检索有关智能合约正在处理的当前交易提议的详细信息。这包括交易标识符和创建交易提议的时间。
      • getTxID( ):返回当前交易建议的id。**(5)**
      • getTxTimestamp( ) :返回应用程序创建当前交易建议时的时间戳。**(5)**
      • getCreator( ) :返回交易建议创建者的原始身份(X.509或其他)。如果这是X.509证书,则通常更适合使用 ctx.ClientIdentity
      • getSignedProposal( ) :返回智能合约正在处理的当前交易提议的签名副本。
      • getBinding( ) :用于防止使用随机数恶意或意外重播交易。(出于实际目的,随机数是由客户端应用程序生成并包含在加密哈希中的随机数。)例如,智能合约可以在**(1)使用此API 来检测交易是否重播(5)**。
      • getTransient( ) :允许智能合约访问应用程序传递给智能合约的临时数据。请参阅交互点**(9)(10)。临时数据是应用程序-智能合约交互的私有数据。它没有记录在分类帐中,通常与私人数据收集(3)**结合使用。
  1. Key APIs - 交互点**(2)(4)**
  1. Event APIs - 交互点**(5)**
    • 用于在处理智能合约期间设置事件
    • 智能合约使用此API将事件添加到交易响应中。请注意,在交易中只能创建一个事件,并且当合同通过 invokeChaincode 相互调用时,该事件必须起源于最外面的合同。这些事件最终记录在区块链上,并在交互点**(11)**发送到侦听应用程序 。
  1. Utility APIs
    • 是有用的API的集合,这些API并不容易放入预定义的类别中,因此我们将它们分组在一起!它们包括检索当前通道名称,并将控制权传递给同一对等方上的不同链码。
    • 任何peer均可使用,来获取应用程序在哪个通道上调用了智能合约 - 交互点**(13)**
    • (不懂)MagnetoCorp组织中的Peer3上安装了多个智能合约。这些智能合约可以使用此API相互调用。智能合约必须并置/collocated;无法在其他对等方上调用智能合约。 - 交互点**(14)**
    • 其中一些API仅在使用低级链码而不是智能合约的情况下使用。这些API主要用于链码输入的详细操作。智能合约 Contract 类会自动为开发人员整理所有的参数。

ClientIdentity

在大多数情况下,提交交易的应用程序将使用X.509证书。

在本示例中,由 CA1 (7)**颁发的X.509证书(6)**被 Isabella **(8)**在其应用程序中用来在交易 t6 **(5)**中签署提案。

ClientIdentity 接收 getCreator() 返回的信息,并将一组X.509实用程序API放在其顶部,以使其更易于在此常见用例中使用。

  • getX509Certificate( ):返回交易提交者的完整X.509证书,包括其所有属性及其值 - 交互点**(6)**
  • getAttributeValue( ):返回特定X.509属性的值,例如,组织单位 OU 或专有名称 DN - 交互点**(6)**
  • assertAttributeValue( ):返回TRUE,如果X.509属性的指定属性具有指定的值 - 交互点**(6)**
  • getID( ):根据交易提交者的专有名称和颁发方CA的专有名称,返回交易提交者的唯一标识id,格式为 x509::{subject DN}::{issuer DN} - 交互点**(6)**。
  • getMSPID( ):返回交易提交者的channel MSP。这使智能合约可以根据提交者的组织身份做出处理决策 - 交互点**(15)(16)**

Transaction handlers

交易处理程序允许智能合约开发人员在应用程序与智能合约之间的交互过程中的关键点定义通用处理。

  • 交易处理程序是可选的,但如果定义,它们将在调用智能合约中的每个交易之前或之后获得控制。即使没有定义也并不影响智能合约的正常运行。
  • 还有一个特定的处理程序,该处理程序在发出请求以调用智能合约中未定义的交易时接收控制。
  • 一个智能合约最多可以定义每种类型的一个handler,如果需要执行多个功能,则需要在功能内进行协调

1、类型:

截屏2021-03-20 下午4.54.53
  • Before handler
    • 在每个智能合约交易被调用之前触发
    • 通常用来修改交易的 context。可以访问所有范围的Fabric API;
    • 例如,它可以issue getState()putState()
  • After handler
    • 在每个智能合约交易被调用之后触发
    • 通常执行所有交易的后加工。可以访问所有范围的Fabric API;
  • Unknown handler
    • 在尝试调用智能合约中没有定义的交易时触发
    • 通常用来记录故障,以供管理员进行后续处理。可以访问所有范围的Fabric API;

2、定义:

交易处理程序将作为具有明确定义名称的方法添加到智能合约中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CommercialPaperContract extends Contract {
...
async beforeTransaction(ctx) {
// Write the transaction ID as an informational to the console
console.info(ctx.stub.getTxID());
};

async afterTransaction(ctx, result) {
// This handler interacts with the ledger
ctx.stub.cpList.putState(...);
};

async unknownTransaction(ctx) {
// This handler throws an exception
throw new Error('Unknown transaction function');
};

}
  • 交易处理程序定义的形式对于所有处理程序类型都是相似的,但是请注意,afterTransaction(ctx, result) 也将接收交易返回的任何结果。
  • API文档您展示了这些处理器的确切形式。

3、过程

将处理程序添加到智能合约后,将在交易处理期间将其调用。在处理期间,处理程序接收ctx, 也就是 transaction context,执行一些处理,并在完成时返回控制。处理继续如下:

  • 在处理程序之前:如果处理程序成功完成,则使用更新的 context 调用交易。如果处理程序引发异常,则不会调用该交易,并且智能合约将失败,显示异常错误消息。

  • 处理程序之后:如果处理程序成功完成,则智能合约将根据所调用的交易确定完成。如果处理程序引发异常,则交易将失败,并显示异常错误消息。

  • 未知处理程序:处理程序应通过引发带有所需错误消息的异常来完成。如果未指定Unknown处理程序,或者未引发异常,则存在显式默认处理;智能合约将失败,并显示未知的交易错误消息。

当处理程序需要访问函数和参数:

1
2
3
4
5
6
7
async beforeTransaction(ctx) {
// Retrieve details of the transaction
let txnDetails = ctx.stub.getFunctionAndParameters();

console.info(`Calling function: ${txnDetails.fcn} `);
console.info(util.format(`Function arguments : %j ${stub.getArgs()} ``);
}

背书策略

背书策略定义了认可交易以使其生效所需的最小组织集合。

  • 要进行背书,组织的背书对等方需要运行与交易关联的智能合约并签署其结果。
  • 当订购服务将交易发送给 committed 的对等方时,他们将分别检查交易中的背书是否满足背书策略。如果不满足,则该交易将无效,并且对世界状态没有任何影响。

背书策略以两种不同的粒度起作用:

  • 可以为整个命名空间以及单个状态键设置它们。它们使用诸如 ANDOR 的基本逻辑表达式来表示。
  • 例如,在PaperNet中,该方法可以按以下方式使用:从MagnetoCorp出售给DigiBank的paper的背书政策可以设置为 AND(MagnetoCorp.peer, DigiBank.peer),要求对paper进行任何更改都必须由MagnetoCorp和DigiBank进行背书。

连接配置

1
gateway.connect(connectionProfile, connectionOptions);

Connection Profile

连接配置文件

应用程序主要使用连接配置文件来配置处理所有网络交互的网关,从而使其能够专注于业务逻辑。

  • 连接配置文件主要中来配置网关,通常由了解网络拓扑的管理员创建。

  • 静态连接配置文件通常由详细了解网络拓扑的管理员创建。这是因为静态配置文件可以包含很多信息,并且管理员需要在相应的连接配置文件中捕获此信息。相反,动态配置文件将所需的定义量减到最少。

  • 静态网关比动态网关需要更多的信息,因为动态网关可以使用服务发现来动态扩展连接配置文件中的信息

  • 连接配置文不应该是网络通道的详尽描述,它只需要包含足够的信息即可使用它的网关

    • 根据需要添加和删除对等方,订购方,证书颁发机构,渠道和组织。
    • 组件可能会启动和停止,或者发生意外故障(例如断电)。
    • 服务发现可以在连接配置文件中扩充信息。具体而言,可以使用最少的Fabric拓扑信息来配置动态网关。其余的可以发现。
  • 语法:json或者yaml

使用

1
2
3
4
5
6
const yaml = require('js-yaml');
const { Gateway } = require('fabric-network');

const connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/paperNet.yaml', 'utf8'));
const gateway = new Gateway();
await gateway.connect(connectionProfile, connectionOptions);
  • 通过使用连接配置文件配置网关,issue.js 将为网关提供应用于处理交易的相关网络拓扑
  • 对于任何给定的组织而言,连接配置文件定义一个以上的对等点都是一种很好的做法 – 它可以防止单点故障。这种做法也适用于动态网关。为服务发现提供多个起点。
  • connectionOptions 传递给网关配合连接配置文件。它们允许应用程序声明网关希望使用连接配置文件的方式。SDK会对它们进行解释,以控制与网络组件的交互模式,例如,选择要与之连接的身份或用于事件通知的对等实体。阅读有关可用连接选项的列表以及何时使用它们的信息。

定义

文件路径commercial-paper/organization/magnetocorp/gateway/paperNet.yaml

结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
---
name: "name"
x-type: "hlfv1"
description: "to descript this connection profile"
version: "1.0.0"
channels:
channelName:
orderers:
- orderer1.orgName.example.com
peers:
peer1.orgName.example.com:
endorsingPeer: true
...
peer2.orgName.example.com:
...

organizations:
org1Name:
mspid: OrgNameMSP
peers:
- peer1.orgName.example.com
- peer2.orgName.example.com
certificateAuthorities:
- ca-orgname
org2Name:
...

orderers:
orderer1.orgName.example.com:
url: grpc://localhost:7050
grpcOptions:
ssl-target-name-override: orderer1.magnetocorp.example.com

peers:
peer1.orgName.example.com:
url: grpc://localhost:7251
grpcOptions:
ssl-target-name-override: peer2.magnetocorp.example.com
request-timeout: 120001
peer2.orgName.example.com:
...

certificateAuthorities:
ca1-orgname:
url: http://localhost:7054
httpOptions:
verify: false
registrar:
- enrollId: admin
enrollSecret: adminpw
caName: ca-orgname

主要信息:

  • 第9行: name: "papernet.magnetocorp.profile.sample"

    这是连接配置文件的名称。尝试使用DNS样式名称;它们是传达意义的一种非常简单的方法。

  • 第16行: x-type: "hlfv1"

    用户可以添加自己 x- 的“application-specific”的属性,就像使用HTTP标头一样。它们主要是为将来使用而提供的。

  • 第20行: description: "Sample connection profile for documentation topic"

    连接配置文件的简短描述。尝试使此功能对可能是第一次看到此内容的读者有所帮助!

  • 第25行: version: "1.0"

    此连接配置文件的架构版本。当前仅支持1.0版,并且没有预见到该架构会经常更改。

  • 第32行: channels:

    这是第一条真正重要的一行。

    channels: 标识此连接配置文件描述的所有通道属性。但是,优良作法是将不同的通道保留在不同的连接配置文件中,尤其是当它们彼此独立使用时。

    • 第36行: papernet:

      下面是有关 papernet 的详细信息,是此连接配置文件中的第一个channel

    • 第41行: orderers:

      papernet 中所有 orderers 的详细信息。

      orderer1.magnetocorp.example.com 为此频道的orderer(45)。这只是一个逻辑名称;稍后在连接配置文件中(第134-147行),将详细介绍如何连接到这个orderer。

      orderer2.digibank.example.com 不在此列表中。应用程序将用自己组织的订购者,而不是其他组织的订购者

    • 第49行: peers:

      papernet 中所有 peers 的详细信息

      你可以看到从MagnetoCorp列出了三个同行: peer1.magnetocorp.example.compeer2.magnetocorp.example.compeer3.magnetocorp.example.com

      • 无需像在此处列出的那样列出在MagnetoCorp的所有peer。比如DigiBank中只列出了一个peer:peer9.digibank.example.com; 现在我们将确认,包括该对等点开始意味着背书策略要求MagnetoCorp和DigiBank背书交易。

      使用时最好有多个peer,以避免单点故障。

      每个peer下面,你可以看到四个非排他性的角色设置:endorsingPeerchaincodeQueryledgerQueryEventSource的

      • 了解peer1以及 peer2如何在担任papercontract主人时扮演所有角色。
      • 相比之下,peer3 只能用于通知或访问分类账的区块链组件而不是世界状态的分类账查询,因此不需要安装智能合约。
      • 请注意peer9,除了背书之外,不应将其用于其他任何用途,因为MagnetoCorp的其他peer可以更好地担当这些角色。

      以上信息是通过peer的逻辑名称和角色来描述的,在配置文件的后面,我们将看到这些peer的物理信息。

  • 第97行: organizations:

    配置所有通道上某组织的属性。请注意,尽管papernet目前是唯一列出的通道,但这些组织适用于所有通道。这是因为组织可以位于多个通道中,并且通道可以具有多个组织。而且,某些应用程序操作与组织有关,而不是与通道有关。

    • 例如,应用程序可以使用连接选项从其组织内的一个或所有对等方或网络内的所有组织请求通知。为此,需要有一个组织到对等方的映射

    • 第101行: MagnetoCorp:

      • 第109 / 116行:peers: / certificateAuthorities:

        列出的所有peer都被认为MagnetoCorp的一部分:peer1peer2peer3

        证书颁发机构也是如此。

        请注意此处仍使用逻辑名称,与本channels:节相同;物理信息将在配置文件的后面列出。

    • 第121行: DigiBank:

      仅列出了DigiBank的一部分peer:peer9

      没有列出证书颁发机构。这是因为DigiBank其他的peer和DigiBank CA均与本连接配置文件的用户无关。

以下是之前相关逻辑名称对应的物理信息

  • 第134行: orderers:

    现在列出了 orderers 的物理信息。由于此连接配置文件仅提及 papernet 的一个订购者,所以只列出了 orderer1.magnetocorp.example.com 的详细信息。

    其中包括其IP地址端口,以及gRPC选项 ssl-target-name-override,可以在需要时覆盖与订购者进行通信时使用的默认值。

    与一样peers:,对于高可用性,指定多个订购者是一个好主意。

  • 第152行: peers:

    现在列出了先前所有对等方的物理信息。此连接配置文件有MagnetoCorp的三个对等方: ,peer1peer2peer3; 对于DigiBank,列出了单个对等方 peer9的信息。

    对于每个对等方,如orderer一样,将列出其IP地址端口,并列出gRPC选项,这些选项可以覆盖与特定对等方进行通信时使用的默认值(如有必要)。

  • 194行: certificateAuthorities:

    现在列出了证书颁发机构的物理信息。连接配置文件为MagnetoCorp列出了一个CA ca1-magnetocorp,其物理信息如下。

    除了IP地址 url: 之外,resigter: 表示还允许将此CA用于证书签名请求(CSR)。这些用于请求本地生成的公用/专用密钥对的新证书。

Connection Options

配置选项:允许应用程序声明网关希望与连接配置文件交互的方式

连接选项与连接配置文件一起使用,以精确控制网关与网络的交互方式。使用网关可以使应用程序专注于业务逻辑,而不是网络拓扑。

网络由连接配置文件标识,配置选项精确指定网关应如何与之交互。

使用

以MagnetoCorp的 issue.js为示例 :

1
2
3
4
5
6
7
8
9
10
11
12
const userName = 'User1@org1.example.com';
const wallet = new FileSystemWallet('../identity/user/isabella/wallet');
const connectionOptions = {
identity: userName,
wallet: wallet,
eventHandlerOptions: {
commitTimeout: 100,
strategy: EventStrategies.MSPID_SCOPE_ANYFORTX
}
};

await gateway.connect(connectionProfile, connectionOptions);
  • identitywallet 选项为 connectionOptions 对象的简单属性 。它们分别具有值 userNamewallet,这些值在代码的前面设置。
  • eventHandlerOptions 本身就是对象的选项。它具有两个属性:
    • commitTimeout: 100:(以秒为单位)
    • strategy: EventStrategies.MSPID_SCOPE_ANYFORTX

选项

develop.diagram.35
  • wallet:标识代表应用程序使用网关的钱包。**(1)** ;

    • 钱包是由应用程序指定的,但实际上是从中检索身份的网关。
    • 必须指定一个钱包;决定使用的钱包类型是最重要的 - 文件系统,内存,HSM还是数据库。
  • identity:是应用程序将使用的来自 wallet 的用户身份。**(2a)** ;

    • 用户身份由应用程序指定,并代表应用程序的用户Isabella**(2b)**。但身份实际上是由网关检索的。
      • 在我们的示例中,不同MSP**(2c)**, **(2d)**将使用Isabella的身份来标识她来自MagnetoCorp,并且在其中具有特定角色。这两个事实将相应地确定她对资源的许可,例如能够读取和写入分类帐。
    • 必须指定用户身份。如您所见,此身份对于Hyperledger Fabric是一个permissioned网络 - 所有参与者都具有一个身份,包括应用程序,peers 和 oderers,这些身份决定了他们对资源的控制。您可以在会员服务主题中阅读有关此想法的更多信息。
  • clientTlsIdentity:是从钱包**(3a)检索并用于网关和不同通道组件(例如peers和orderers)之间的安全通信(3b)identity**。

    • 请注意,此identity不同于用户身份。尽管 clientTlsIdentity 对于安全通信很重要,但它不如用户身份那么基础,因为它的范围不会扩展到安全网络通信之外。
    • clientTlsIdentity是可选的。建议您在生产环境中进行设置。您应该始终使用跟 identity不同的 clientTlsIdentity,因为这些身份具有不同的含义和生命周期。
      • 例如,如果你的 clientTlsIdentity 受到了损害,那么你的 identity 也会受到损害。将它们分开是更安全的。
  • eventHandlerOptions

    • commitTimeout:是可选的。它以秒为单位指定在将控制权返回给应用程序之前,网关应等待对等方**(4a)**提交交易的最长时间(默认为300s)。

    • strategy:是可选的。它标识网关应用于侦听交易已提交的通知的对等体的集合。例如,是侦听组织中的单个对等方还是所有对等方。它可以采用以下值之一:

      • EventStrategies.MSPID_SCOPE_ANYFORTX:侦听用户组织内任何对等方**(4b)**。MagnetoCorp的对等体1,对等体2或对等体3中的任何一个都可以通知网关。

      • EventStrategies.MSPID_SCOPE_ALLFORTX: 这是默认值。监听用户组织内所有对等方**(4b)。MagnetoCorp的所有对等方都必须**已通知网关。不包括stoppedfalied的对等方。

      • EventStrategies.NETWORK_SCOPE_ANYFORTX:侦听整个网络通道中的任何对等方**(4b)(4c)**。MagnetoCorp的对等方1-3或DigiBank的对等方7-9中的任何一个都可以通知网关。

      • EventStrategies.NETWORK_SCOPE_ALLFORTX:侦听整个网络通道中的所有对等方**(4b)(4c)。MagnetoCorp和DigiBank的所有对等方都必须**通知网关;对等1-3和对等7-9。不包括stoppedfalied的对等方。

      • < PluginEventHandlerFunction>:用户定义的事件处理程序的名称。这允许用户定义自己的事件处理逻辑。了解如何定义事件处理程序插件,并检查样本程序

        仅当有非常特定的事件处理要求时,才需要用户定义的事件处理程序。通常,使用内置事件策略之一就足够了。用户定义的事件处理程序的一个示例可能是等待组织中超过一半的对等方来确认交易已提交。

        如果确实指定了用户定义的事件处理程序,则它不会影响您的应用程序逻辑。它与这完全不同。SDK在处理过程中会调用处理程序;它决定何时调用它,并使用其结果选择要用于事件通知的对等方。SDK完成处理后,应用程序将收到控制权。

        如果未指定用户定义的事件处理程序,则使用EventStrategies的默认值

  • discovery

    • enabled:是可选的,并且可能具有true或值false。默认值为true。它确定网关是否使用服务发现来扩展连接配置文件中指定的网络拓扑。**(6)** ; 网关使用的对等方gossip information。
      • 该值会被 INITIALIIZE-WITH-DISCOVERY 环境变量覆盖
    • asLocalhost:是可选的,并且可能具有true或值false。默认值为true。它确定是否把在服务发现期间找到的IP地址从docker网络转换为本地主机。
      • 通常,开发人员会编写使用docker容器作为其网络组件(例如peer,orderer和CA)的应用程序,但这些应用程序本身不会在docker容器中运行。这就是 true 默认设置的原因;
      • 在生产环境中,应用程序可能会以与网络组件相同的方式在docker容器中运行,因此不需要地址转换。在这种情况下,应用程序应显式指定 false 或使用环境变量替代。
      • 该值将被 DISCOVERY-AS-LOCALHOST 环境变量覆盖

注意事项:

下面的注意事项列表在决定如何选择连接选项时会很有帮助。

  • eventHandlerOptions.commitTimeouteventHandlerOptions.strategy一起工作。

    • 例如,commitTimeout: 100strategy: EventStrategies.MSPID_SCOPE_ANYFORTX表示网关将等待多达100秒,以便任何一个对等方确认交易已提交。相反,strategy: EventStrategies.NETWORK_SCOPE_ALLFORTX 意味着网关将对所有组织中的所有对等方最多等待100秒。
  • 默认值 eventHandlerOptions.strategy: EventStrategies.MSPID_SCOPE_ALLFORTX 将等待应用程序组织中的所有对等方提交交易。

    • 这是一个很好的默认设置,因为应用程序可以确保所有对等方都拥有分类账的最新副本,从而最大程度地减少了并发问题
    • 但是,随着组织中对等方数目的增加,等待所有对等方变得有点不必要,在这种情况下,使用**<可插入事件处理程序>**可以提供更有效的策略。例如,在共识将使所有分类帐保持同步的安全假设下,可以使用相同的一组对等方来提交交易和侦听通知。
  • 需要 clientTlsIdentity 来设置服务发现。

    • 这是因为与应用程序交换信息的对等方需要确信他们正在与自己信任的实体交换信息。如果 clientTlsIdentity未设置,则无论 discovery 是否设置,都将不服从。
  • 尽管应用程序可以在连接到网关时设置连接选项,但是管理员可能有必要覆盖这些选项。

    • 这是因为选项与网络交互有关,该交互可能随时间而变化。例如,管理员试图了解使用服务发现对网络性能的影响。
    • 一种好的方法是在一个配置文件中定义应用程序的重写,当应用程序配置其与网关的连接时,该配置文件将由应用程序读取。
    • 因为发现选项enabledasLocalHost是管理员最经常需要覆盖的选项,所以提供了环境变量 INITIALIIZE-WITH-DISCOVERYDISCOVERY-AS-LOCALHOST以便于使用。管理员应在应用程序的生产运行时环境中设置这些设置,该环境很可能是docker容器。

wallet

钱包包含一组用户身份。用户运行的应用程序在连接到通道时会选择这些身份之一。结合MSP使用此身份确定对诸如分类帐之类的信道资源的访问权限。

过程:

develop.diagram.10
  • 当应用程序连接到诸如PaperNet之类的网络通道时,它会选择一个用户身份进行连接ID1。通道MSP将ID1与特定组织内的角色相关联,并且该角色最终将确定应用程序对通道资源的权利。例如,ID1可能将某个用户标识为可以读写账本的MagnetoCorp组织成员,而ID2可能会标识MagnetoCorp中可以向联盟添加新组织的管理员。

类型

钱包存储的三种不同类型:文件系统,内存和CouchDB。

  • 文件系统:这是最常见的存放钱包的地方;文件系统无处不在,易于理解,并且可以通过网络安装。对于钱包来说,它们是一个很好的默认选择。
  • 内存中:应用程序存储中的钱包。当您的应用程序在受限环境中运行而无法访问文件系统时,请使用这种钱包。通常是网络浏览器。值得记住的是,这种钱包是易变的。应用程序正常结束或崩溃后,身份将丢失。
  • CouchDB:存储在CouchDB中的钱包。这是最罕见的钱包存储形式,但是对于那些想要使用数据库备份和还原机制的用户,CouchDB钱包可以提供一个有用的选项来简化灾难恢复。

使用wallet类的工厂函数创建钱包

HSM

硬件安全模块(HSM)是一种超安全的防篡改设备,用于存储数字身份信息,尤其是私钥。HSM可以本地连接到您的计算机或可通过网络访问。大多数HSM提供了使用私钥执行机载加密的功能,这样私钥就永远不会离开HSM。

  • HSM可以与任何钱包类型一起使用。在这种情况下,身份证书将存储在钱包中,私钥将存储在HSM中。

  • 要启用使用HSM管理的身份,IdentityProvider 必须使用HSM连接信息配置并在钱包中注册。有关更多详细信息,请参阅《使用钱包管理身份》教程。

结构

单个钱包可以保存多个身份,每个身份由特定的证书颁发机构颁发。每个身份具有一个标准结构,该标准结构包括描述性标签公钥私钥和某些特定于Fabric的metadata的X.509证书。不同的钱包类型将此结构适当地映射到其存储机制。

develop.diagram.11

  • Fabric钱包可以使用由不同证书颁发机构颁发的证书来保存多个身份。
    • 身份包括证书私钥结构元数据

生成钱包:

1
2
3
4
5
6
7
8
9
10
const identity: X509Identity = {
credentials: {
certificate: certificatePEM,
privateKey: privateKeyPEM,
},
mspId: 'Org1MSP',
type: 'X.509',
};
// const identityLabel = 'isabella';
await wallet.put(identityLabel, identity);

获取身份:

  • Gateway 类只需要 mspIdtype 来标识一个身份,它使用MSP ID在连接配置文件中识别一个对等方
1
2
3
4
5
6
organizations:
Org1:
mspid: Org1MSP

peers:
- peer0.org1.example.com
  • 通过 get() 方法获得身份 fabric-network.Identit,没有则返回 undefined
1
2
// const identityLabel = 'isabella';
const userExists = await wallet.get('identityLabel');

运行方式

不同的钱包类型都实现了一个通用的电子钱包接口,该接口提供了一组标准的API来管理身份。

生命周期:可以创建或打开钱包,并且可以读取,添加和删除身份。

示例代码commercial-paper/organization/magnetocorp/application/addToWallet.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');

const cert = fs.readFileSync(path.join(credPath, '.../User1@org1.example.com-cert.pem')).toString();
const key = fs.readFileSync(path.join(credPath, '.../_sk')).toString();

const identityLabel = 'User1@org1.example.com';
const identity = {
credentials: {
certificate: cert,
privateKey: key,
},
mspId: 'Org1MSP',
type: 'X.509',
};

await wallet.put(identityLabel, identity);
  • 首次运行该程序时,会在本地文件系统上创建一个钱包 .../isabella/wallet
  • 证书cert和私有key文件是从文件系统加载的。
  • 新的X.509身份使用 certkeyOrg1MSP 来创建。
  • 使用 wallet.put() 将带有标签 User1@org1.example.com 的新身份添加到钱包中

Gateway

网关代表应用程序管理网络交互,使其专注于业务逻辑。应用程序连接到网关,然后使用该网关的配置管理所有后续交互。

使用方式:

  1. 静态:网关配置完全在连接配置文件中定义。
    • 应用程序可用的所有对等方订购者CA在用于配置网关的连接配置文件中静态定义。例如,对于对等方来说,包括其作为 endorsing peer 或event notification hub的角色。
    • SDK将结合网关连接选项来使用此静态拓扑来管理交易的提交和通知过程。连接配置文件必须包含足够的网络拓扑,以允许网关代表应用程序与网络交互;这包括网络渠道,组织,订购者,对等方及其角色。
  1. 动态:网关配置只在连接配置文件中最少定义。
    • 通常,指定来自应用程序组织的一个或两个对等方,并且它们使用服务发现来发现可用的网络拓扑。这包括对等方,订购者,渠道,已部署的智能合约及其认可策略。(在生产环境中,网关配置应至少指定两个对等方以确保可用性。)
    • SDK将使用文件中所有的静态定义部分和发现的拓扑信息以及网关连接选项来管理交易的提交和通知过程。作为其一部分,它还将智能地使用发现的拓扑。例如,它将使用发现的智能合约背书策略计算所需的最少背书对等体。

静态或动态网关是否更好?权衡是在可预测性和响应性之间。静态网络始终会以相同的方式运行,因为它们将网络视为不变的。从这种意义上讲,它们是可预测的–如果可用,它们将始终使用相同的对等方和订购方。当动态网络了解网络的变化时,它们的响应速度更快–它们可以使用新添加的对等点和订购者,从而带来额外的弹性和可扩展性,但可能会在可预测性方面付出一些代价。通常,使用动态网络是可以的,实际上,这是网关的默认模式。

  • 请注意,可以静态或动态使用同一连接配置文件。显然,如果要静态使用配置文件,则它必须是全面的,而动态使用仅需要稀疏的填充即可。
  • 两种样式的网关对应用程序都是透明的。无论使用静态还是动态网关,应用程序设计都不会改变。这也意味着某些应用程序可以使用服务发现,而其他应用程序则可以不使用。通常,使用动态发现意味着SDK的定义更少,情报更多。这是默认值。

连接:(前面有详细的)

1
await gateway.connect(connectionProfile, connectionOptions);
  • 连接配置文件connectionProfile 是网关配置,它将用于SDK的静态或动态交易处理。尽管可以在传递给网关时将其转换为JSON对象,但是可以使用YAMLJSON指定它:

    1
    let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/paperNet.yaml', 'utf8'));
  • 连接选项connectionOptions 允许应用程序声明而不是实现所需的事务处理行为。SDK会解释连接选项,以控制与网络组件的交互模式,例如,选择要与之连接的身份或用于事件通知的对等对象。这些选项可在不影响功能的情况下显着降低应用程序的复杂性。这是可能的,因为SDK已实现了许多应用程序原本需要的低层逻辑。连接选项控制此逻辑流。

static

静态网关定义网络的固定视图

在MagnetoCorp 方案中,网关可能会标识MagnetoCorp的单个对等方,DigiBank的单个对等方和MagentoCorp订购者。或者,网关可以定义MagnetCorp和DigiBank的 所有 对等方和订购方。在这两种情况下,网关都必须定义足以使商业票据交易获得认可和分发的网络视图。

  • 通过在API gateway.connect() 上显式指定connect选项 discovery: { enabled:false },应用程序可以静态使用网关。或者,环境变量设置 FABRIC_SDK_DISCOVERY=false 将始终覆盖应用程序选择。
  • 检查 MagnetoCorp的 issue.js 应用程序使用的连接配置文件。查看如何在此文件中指定所有对等方,订购者甚至CA,包括它们的角色。

注意:静态网关代表某个时刻的网络视图。随着网络的变化,将其反映在网关文件的变化中可能很重要。当应用程序重新加载网关文件时,它们将自动获取这些更改。

dynamic

动态网关为网络定义了一个最小的固定起点

在MagnetoCorp方案中,动态网关可能仅识别MagnetoCorp中的单个对等方。一切都会被发现!(为了预防单点故障,最好定义两个这样的引导对等点)

  • 如果应用程序选择了服务发现,则网关文件中定义的拓扑将使用此过程生成的拓扑进行扩充。
    • 服务发现从网关定义开始,并使用 gossip 协议在MagnetoCorp组织中找到所有连接的对等方和订购方。
    • 如果已为通道定义了anchor peer,则服务发现将使用跨组织的 gossip 协议来发现连接的组织内的组件。此过程还将发现在对等方安装的智能合约及其在渠道级别定义的认可策略。
    • 与静态网关一样,发现的网络必须足以批准和分发商业票据交易。
  • 动态网关是Fabric应用程序的默认设置。可以使用API gateway.connect() 上的连接选项 discovery: { enabled:true } 明确指定它们。或者,环境变量设置 FABRIC_SDK_DISCOVERY=true 将始终覆盖应用程序选择。

注意:动态网关代表网络的最新视图。随着网络的变化,服务发现将确保网络视图正确反映了应用程序可见的拓扑。应用程序将自动获取这些更改;他们甚至不需要重新加载网关文件。

multiple

对于应用程序而言,定义相同或不同网络的多个网关非常简单。此外,应用程序可以静态和动态使用名称网关。

拥有多个网关可能会有所帮助。原因如下:

  • 代表不同用户处理请求。
  • 同时连接到不同的网络。
  • 通过同时比较其行为与现有配置来测试网络配置。
  • Post title:fabric-Tutorials
  • Post author:Wei Jieyang
  • Create time:2021-03-19 11:33:34
  • Post link:https://jieyang-wei.github.io/2021/03/19/fabric-Tutorials/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.