fabric网络运行过程中动态追加新的组织是相当复杂的,网上的资料也十分匮乏,大多是基于first-network这样的简单示例,而且是使用启动cli容器的方法来增加组织,几乎没有针对实际应用的解决方案。本文介绍了如何在应用程序中调用SDK来进行组织的动态增加。

前言

首先需要介绍一个配置区块的概念,fabric中的配置信息是作为区块写在链上的,每个配置区块中只有一条配置交易,而且配置区块是全量更新的,最新的配置区块中应包含全部的配置信息。

回忆一下在创建通道时,会从本地读取通道配置交易(根据configtx.yaml生成),这个配置交易中指定了该通道中有哪些组织,以及设置了各组织的证书信息。如果想要在后续进行添加,就必须要让当前通道认可这个新组织,则需要提交一个包含新加组织的配置区块来对当前配置进行更新。

大致思路是首先从节点中获取到当前通道的最新配置区块,利用configtxlator工具将配置信息由protobuf格式转化为可读的json格式,手动在配置中添加上新组织的配置,然后再使用该工具计算修改前后的差值,将这个增量作为通道更新的请求发送出去。同时,这个通道更新的请求需要超过半数的当前组织签名才算有效。

调用SDK增加组织

因为是在fabric实际应用中增加组织,所以通过在app中编写代码调用SDK来完成所有操作是最优的方案。而且一旦实现,在之后的应用开发中可以很方便地复用,再配合上一些自动化脚本可以使繁杂的操作变得简单化,做到轻松的增加或删除网络内的组织。

值得一提的是,官方的node-sdk中提供了一段关于通道更新的例子configtxlator.js,不过里面实现的是删除某个组织,我们可以做一些改动来实现添加组织。

本文以balance-transfer v1.0为例,介绍如何通过调用Node SDK的方法,在已有两个组织的基础上增加新组织Org3,其中包含1个CA节点,2个Peer节点。

一、生成新组织证书目录

因为进入fabric网络是需要身份的,所以不论是加入新节点还是加入新组织,都要为新增的成员生成MSP目录。在artifacts/channel目录下创建新组织的配置文件org3-crypto.yaml

PeerOrgs:
- Name:Org3
Domain: org3.example.com
CA:
Hostname: ca
Template:
Count: 2
SANS:
- "localhost"
Users:
Count: 1

接着利用cryptogen工具生成Org3的msp目录,并输出到crypto-config目录中:

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

二、编写Nodejs代码调用SDK

我在app目录下创建了一个单独的文件add-org.js来完成添加组织,下面只提供程序的主要思路,细节可参考详细代码

1.安装所需Node模块

由于要在Nodejs程序中发送REST请求给configtxlator工具,所以需要事先安装模块(类似于curl):superagentsuperagent-promiserequest,其中request建议使用v1.9.8版本。导入模块:

var requester = require('request');
var agent = require('superagent-promise')(require('superagent'), Promise);

2.获取当前配置区块

调用getChannelConfig接口获取到最新的配置信息,接收到的结果是ConfigEvelope类型的对象:

var config_envelope = await channel.getChannelConfig()

我们只需要用到其中的config部分,取出后将其转化为二进制,注意original_config_proto是原始的配置信息,会在后面计算差值时用到。

var original_config_proto = config_envelope.config.toBuffer();

3.利用工具转化为json格式

使用configtxlator工具进行protobuf和json之间的转换,利用superagent-promise发出请求:

var response = await
agent.post('http://127.0.0.1:7059/protolator/decode/common.Config',original_config_proto).buffer();

对响应结果进行处理:

var original_config_json = response.text.toString()     // json string
var updated_config = JSON.parse(updated_config_json) // json object

4.手动增加新组织的信息

我们需要仿照已有的两个组织的配置结构添加上新组织的信息,首先复制Org1MSP部分的内容,注意这里通过先stringify再parse的方式完成一次深拷贝。

var new_config = JSON.parse(JSON.stringify(updated_config.channel_group.groups.Application.groups["Org1MSP"]));

接下来就是在new_config中做相关修改,主要包括两部分,一是所有跟组织名称有关的地方,都需要将Org1替换为Org3;二是将相关证书的值替换成Org3的MSP目录中的实际证书的内容(从文件中读取后还需要进行base64编码),三种证书的路径如下(当前位于app目录下,这里使用相对路径):

// 1.admins:组织管理员证书
'../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/msp/admincerts/Admin@org3.example.com-cert.pem'
// 2.root_certs:根CA证书
'../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/msp/admincerts/ca.org3.example.com-cert.pem'
// 3.tls_root_certs:tls根证书
'../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/msp/admincerts/tlsca.org3.example.com-cert.pem'

需要修改的具体位置这里就不方便一一展开了,细节还是参考下代码。在编写js代码的时候可以将配置信息的json对象打印出来,对比下已有组织的配置内容,就可以很直观的找到那些需要替换的地方了。

完成编辑之后,将新组织的配置new_org当前到原有配置update_config上:

updated_config.channel_group.groups.Application.groups["Org3MSP"] = new_config;

并转化为json字符串:

updated_config_json = JSON.stringify(updated_config);

5.利用工具将json格式转为pb格式

response = await
agent.post('http://127.0.0.1:7059/protolator/encode/common.Config', updated_config_json.toString()).buffer();
var updated_config_proto = response.body; // 响应结果:pb格式

6.利用工具计算差值

通道更新请求需要的参数并不是新的配置信息,而是新配置与原始配置的一个差值,需要再次利用configtxlator工具计算这个增量。

首先构造向工具发送的请求体的结构,需要附带我们原始获取的配置original_config_proto以及修改过后的配置updated_config_proto,两者都是pb格式:

var formData = {
channel: channel_name,
original: {
value: original_config_proto,
options: {
filename: 'original.proto',
contentType: 'application/octet-stream'
}
},
updated: {
value: updated_config_proto,
options: {
filename: 'updated.proto',
contentType: 'application/octet-stream'
}
}
};

通过request模块发送post请求:

requester.post({
url:'http://127.0.0.1:7059/configtxlator/compute/update-from-configs',
encoding: null,
headers: {
accept: '/',
expect: '100-continue'
},
formData: formData
}

计算的结果转化为二进制以后赋值给变量config_proto,这就是通道配置的更新增量,下面会作为通道更新请求的重要参数。

7.对配置更新增量进行签名

更新通道的请求需要超过半数的已有组织的管理员身份签名,现有两个组织,则需要两个签名。调用help.js里的getOrgAdmin()方法可以给client对象分配管理员用户对象,然后调用SDK中的signChannelConfig()对配置进行签名:

var signatures = []
for (let org of cur_orgs) {
let client = helper.getClientForOrg(org)
await helper.getOrgAdmin(org) // 给client分配管理员对象
let signature = client.signChannelConfig(config_proto);
signatures.push(signature)
}

其中cur_orgs参数是除Orderer外所有组织名的集合,这里用了一个循环让所有组织管理员对配置签名。

8.发送更新通道的请求

首先构造请求体:

let tx_id = client.newTransactionID();
var request = {
config: config_proto, // 配置更新增量
signatures: signatures, // 组织管理员签名
name: channel_name,
orderer: channel.getOrderers()[0],
txId: tx_id
};

调用SDK的updateChannel()接口对通道进行更新,该方法在内部会将新的配置交易发送到orderer节点,打包成配置区块后分发给当前所有peer节点,peer节点将新的配置区块存入链中,此时该通道就接受认可了新加入的组织。

var result = await client.updateChannel(request);

三、执行代码加入新组织

Nodejs代码编写完成后整个工作就成功了一大半,接下来需要执行该程序,将Org3加入到当前网络。

首先启动configtxlator服务,默认监听7059端口:

configtxlator start

然后运行我们的Nodejs程序:

node add_org.js

成功响应后说明新组织加入成功,此时链上会生成一个新的配置区块。

四、更新配置文件

1.创建CA服务器配置文件

新加的组织Org3也拥有一个属于自己的CA节点,在之前的修改组织名的文章中已经介绍了如何设置CA服务器配置文件fabric-ca-server-config.yaml(主要是affiliations部分需要修改),以及如何在docker-compose文件中将该文件映射到CA容器内部。我的Github中也保存了该配置文件的模板

2.编写容器配置文件启动新组织节点

现在启动Org3中的节点,首先需要编写docker-compose文件。这一步比较简单,只要模仿已有组织的docker-compose.yaml文件即可。

Org3包含一个CA节点,两个Peer节点。编写该配置文件需要注意:如果所有组织都在一个机器上,则要保证容器的端口不会冲突。而且CA容器中的CA_KEYFILETLS_KEYFILE两个参数要和实际新组织的msp目录中的私钥文件路径一致。最后不要忘记添加CA服务器配置文件的映射。

将已完成Org3的配置文件docker-compose-org3.yaml置于artifacts目录下,执行以下命令启动三个节点:

docker-compose -f docker-compose-org3.yaml up -d

3.修改网络配置文件network-config.json

该文件路径为app/network-config.json,主要设置了网络各组织节点的ip和port信息,用于应用程序与网络节点进行交互。

需要仿照已有的组织,添加上新加入组织的信息,Org3部分大致如下:

"Org3": {
"name": "peerOrg3",
"mspid": "Org3MSP",
"ca": "https://localhost:7054",
"peers": {
"peer1": {
"requests": "grpcs://localhost:9051",
"events": "grpcs://localhost:9053",
"server-hostname": "peer0.org3.example.com",
"tls_cacerts": "../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt"
},
"peer2": {
"requests": "grpcs://localhost:9056",
"events": "grpcs://localhost:9058",
"server-hostname": "peer1.org3.example.com",
"tls_cacerts": "../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/tls/ca.crt"
}
},
"admin": {
"key": "../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/keystore",
"cert": "../artifacts/channel/crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/signcerts"
}
}

五、将新组织中的节点加入通道

新组织的节点容器已经启动,首先需要在Org3注册某个用户,拿到Org3的TOKEN,这里设为ORG3_TOKEN,然后发送请求把Org3中的两个节点加入到通道中:

curl -s -X POST \
http://localhost:4000/channels/mychannel/peers \
-H "authorization: Bearer $ORG3_TOKEN" \
-H "content-type: application/json" \
-d '{
"peers": ["peer1","peer2"]
}'

六、升级链码

目前新组织节点没有安装链码,只能参与记账,无法指定其完成查询或交易操作。但是即使安装了旧版本的链码,会发现节点可以查询,但是进行的交易确是无效的。

这是因为在chaincode实例化的时候会指定背书策略,默认是channel其中一个组织的某一个成员进行背书,但是该背书策略中没有包含后续新加入的组织,所以在验证阶段会被标记成invalid,能一直产生区块,却不会写入状态数据库。

所以如果需要新加组织的节点来执行交易,则需要对链码进行升级,不改变链码内容,只改变版本和背书策略,为的就是在背书策略中加入新组织。

利用SDK来upgrade chaincode也并非易事,需要自行编写js代码来实现。升级链码和实例化链码很相似,都需要生成一个交易。SDK中提供了sendUpgradeProposal()方法来发送升级链码的提案,我们可以参考balance-transfer中的instantiateChaincode.js(链码实例化)代码来编写升级链码的代码,详细接口代码可见github

首先需要设置新的背书策略,该背书策略表示只要3个组织中的其中一个组织的任意一个节点对某个交易背书,该交易就满足策略。

var endorsement_policy =
{
identities: [
{ role: { name: "member", mspId: "Org1MSP" }},
{ role: { name: "member", mspId: "Org2MSP" }},
{ role: { name: "member", mspId: "Org3MSP" }}
],
policy: {
"1-of": [{ "signed-by": 0 }, { "signed-by": 1 }]
}
}

接下来构造升级链码的请求:

var request = {
"chaincodeId": "mycc",
"chaincodeVersion": "v1",
"args": [''],
"txId": client.newTransactionID(),
"endorsement-policy": endorsement_policy
};

然后发送提案到背书节点:

var results = channel.sendUpgradeProposal(request);

最后和交易流程一样,需要根据提案响应生成交易,发送到排序服务节点:

var sendPromise = channel.sendTransaction(txRequest);

成功后通道会接受新版本的链码,在Org3安装新链码后可以指定其节点进行有效的查询和交易操作。至此,添加新组织成功!

实际应用开发中的实现

应用开发中应该优先选择上述利用js脚本增加组织的方法。当然也可以使用cli容器的方法,最好要写一个脚本,自动启动cli容器,完成上述所有操作以后再删除cli容器,不过相比调用SDK还是有诸多不便。

我在实际开发中是将添加或删除组织升级链码这两个功能加入了应用程序代码中,写成了RESTful接口,客户端可以通过http请求来完成这两个操作。

并且还写了一个shell脚本,来自动化执行一些操作,包括生成证书,启动configtxlator工具,发送更改组织的请求,关闭工具等。如果进一步完善,甚至可以将后续修改配置文件等操作也加入脚本中,达到一键执行就能够完成增加或者删除组织的效果。

上述代码可以在我的Github中找到:https://github.com/zhayujie/fabric-tools

原文链接:https://zhayujie.com/add-org.html

Fabric进阶(三)—— 使用SDK动态增加组织的更多相关文章

  1. Hyperledger Fabric 动态增加组织到网络中

    本文基于Hyperledger Fabric 1.4版本. 官方文档地址:传送门 动态添加一个组织到Fabric网络中也是一个比较重要的功能.官方文档写的已经很详细了,有能力的尽量还是看官方文档,本文 ...

  2. HyperLedger Fabric 1.4 生产环境动态添加组织及节点

    网易云课堂视频在线教学,地址:https://study.163.com/course/introduction/1209401942.htm 1.1 操作概述      在“kafka生产环境部署” ...

  3. Fabric动态增加组织【资料】

    Fabric在启动之前需要生成Orderer的创世区块和channel的配置区块.也就是说在Fabric网络启动之前我们就必须定好了有哪些Org,而当Fabric已经跑起来之后,想要增加Org却是很麻 ...

  4. 【干货干货】hyperledger fabric 之动态添加组织/修改配置 (Fabric-java-sdk) 下

    我们接着上一节来讲: 在熟悉动态增加组织或修改配置的步骤后,我们就可以使用java的api来完成动态增加组织或修改配置了: 废话不多说,直接上干货: 1,预制条件 org3的证书以及组织3的MSP详情 ...

  5. Fabric进阶(二)—— 在已有组织中增加节点

    fabric网络在创建时就已经确定了初始的节点数量,而在实际应用场景中可能会需要在某个组织中动态增加节点.这里以balance-transfer v1.0为例(2 Org,4 Peer),介绍如何在o ...

  6. 菜鸟系列Fabric——Fabric 动态添加组织(7)

    Fabric 网络动态添加组织 1.环境准备 如果存在fabric网络环境可不执行,若不存在可以安装下列进行准备 下载fabric-sample,fabric https://github.com/h ...

  7. Java进阶(三十九)Java集合类的排序,查找,替换操作

    Java进阶(三十九)Java集合类的排序,查找,替换操作 前言 在Java方向校招过程中,经常会遇到将输入转换为数组的情况,而我们通常使用ArrayList来表示动态数组.获取到ArrayList对 ...

  8. Hyperledger fablic 1.0 在centos7环境下的安装与部署和动态增加节点

    Hyperledger fablic 1.0 在centos7环境下的安装与部署和动态增加节点 一.安装docker 执行代码如下: curl -sSL https://get.daocloud.io ...

  9. hdfs以及hbase动态增加和删除节点

    一个知乎上的问题:Hbase的Region server和hadoop的datanode是否可以部署在一台服务器上?如果是的话,二者是否是一对一的关系?部署在同一台服务器上,可以减少数据跨网络传输的流 ...

随机推荐

  1. opencv-0-项目启程

    opencv-0-项目启程 opencvC++QT 开始 无数次说要开始 opencv 的系列, 但是都由于各种原因没有坚持下去, 这次我做最后一次尝试, 也做最后一次坚持, 如果不做下去, 我就再也 ...

  2. 徐州A

    #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) #defi ...

  3. css之Grid Layout详解

    css之Grid Layout详解 CSS Grid Layout擅长将页面划分为主要区域,或者在从HTML基元构建的控件的各个部分之间定义大小,位置和图层之间的关系. 与表格一样,网格布局使作者能够 ...

  4. 【Linux常见命令】xargs命令

    xargs - build and execute command lines from standard input. 从标准输入< 方向获取数据,再创建和执行命令 xargs 是给命令传递参 ...

  5. 细数阿里云在使用 Docker 过程中踩过的那些坑

    昨天下午道哥在微信上丢给我一条新闻,看看,我们阿里云支持 Docker 企业版了.我打开一看,果然,阿里云发布了飞天敏捷版,开始支持企业级的 Docker 容器. 美国中部时间4月19日,阿里云在容器 ...

  6. JavaScript实现插入排序

    一.插入排序简介: 想象我们斗地主,摸排阶段,手里的牌都按照从小到大排序.如果每摸一张牌,我们就把他插入合适的位置,使得它比后面位置的牌小,比前面位置的牌大或者相等. 类似这样的一种排序方法就是插入排 ...

  7. Jaba_Web--JDBC 删除记录操作模板

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  8. 用Eclipse开发项目,你不能不知道的快捷键

    1. 编辑快捷键 编辑快捷键 介绍 psvm + Tab 生成main方法 sout + tab 生成输出语句 Ctrl+X / Ctrl + Y 删除一行 Ctrl+D 复制一行 Ctrl+/ 或 ...

  9. 编写简单的内核模块及内核源码下载,内核模块Makefile编写

    CentOS的内核源码默认是没有下载的,需要自己下载,首先安装linux的时候就应该知道linux的版本,我装的是Centos7的 下面查一下内核的版本,使用下面的命令 [scut_lcw@local ...

  10. php接口开发经验总结

    接口开发采用jwt方式验证: 接口开发最好留多几个返回方式,方便本地调用: 待更新.............