详细解析blance transfer示例的创建通道(Channel)和加入节点到通道的过程。

创建Channel

1.首先看app.js的路由函数

  1. var createChannel = require('./app/create-channel.js');
  2. app.post('/channels', async function(req, res) {
  3. // 接收参数channel名称和配置文件的路径
  4. // 通道配置交易路径: ./artifacts/channel/mychannel.tx
  5. var channelName = req.body.channelName;
  6. var channelConfigPath = req.body.channelConfigPath;
  7. if (!channelName) {
  8. res.json(getErrorMessage('\'channelName\''));
  9. return;
  10. }
  11. if (!channelConfigPath) {
  12. res.json(getErrorMessage('\'channelConfigPath\''));
  13. return;
  14. }
  15. // 创建通道(username和orgname参数从token中获取)
  16. let message = await createChannel.createChannel(channelName, channelConfichannelNamegPath, req.username, req.orgname);
  17. res.send(message);
  18. });

2.再来看create-channel.js中的createChannel()方法

  1. // 通过发送交易的方法向orderer节点发送创建channel的请求
  2. var createChannel = async function(channelName, channelConfigPath, username, orgName) {
  3. try {
  4. // 生成组织的客户端实例
  5. var client = await helper.getClientForOrg(orgName);
  6. // 从channel配置交易(mychannel.tx)读取源字节
  7. var envelope = fs.readFileSync(path.join(__dirname, channelConfigPath));
  8. // 提取channel配置信息
  9. var channelConfig = client.extractChannelConfig(envelope);
  10. // 客户端使用 组织管理员 的私钥 签名,作为背书(endorsement)
  11. // "../artifacts/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts"
  12. // 在内部通过_getSigningIdentity(true)获取org admin的私钥 进行签名
  13. let signature = client.signChannelConfig(channelConfig);
  14. // 请求结构:
  15. let request = {
  16. config: channelConfig,
  17. signatures: [signature],
  18. name: channelName,
  19. txId: client.newTransactionID(true)
  20. };
  21. // 发送给orderer节点
  22. var response = await client.createChannel(request)
  23. if (response && response.status === 'SUCCESS') {
  24. logger.debug('Successfully created the channel.');
  25. let response = {
  26. success: true,
  27. message: 'Channel \'' + channelName + '\' created Successfully'
  28. };
  29. return response;
  30. } else {
  31. throw new Error('Failed to create the channel \'' + channelName + '\'');
  32. }
  33. } catch (err) {
  34. logger.error('Failed to initialize the channel: ' + err.stack ? err.stack : err);
  35. throw new Error('Failed to initialize the channel: ' + err.toString());
  36. }
  37. };

加入节点到Channel

1.首先看app.js里的路由函数

  1. // 首先看app.js中的路由函数
  2. app.post('/channels/:channelName/peers', async function(req, res) {
  3. // 接收参数 channel名称(URL中)和peers(body中)
  4. var channelName = req.params.channelName;
  5. var peers = req.body.peers;
  6. if (!channelName) {
  7. res.json(getErrorMessage('\'channelName\''));
  8. return;
  9. }
  10. if (!peers || peers.length == 0) {
  11. res.json(getErrorMessage('\'peers\''));
  12. return;
  13. }
  14. // 将peers加入channel
  15. let message = await join.joinChannel(channelName, peers, req.username, req.orgname);
  16. res.send(message);
  17. });

2.然后看join-channel.js中的joinChannel()函数

  1. // 然后看join-channel.js中的joinChannel()函数
  2. var joinChannel = async function(channel_name, peers, username, org_name) {
  3. var error_message = null;
  4. var all_eventhubs = [];
  5. try {
  6. // 首先创建client对象
  7. var client = await helper.getClientForOrg(org_name, username);
  8. // 生成channel对象
  9. var channel = client.getChannel(channel_name);
  10. if(!channel) {
  11. let message = util.format('Channel %s was not defined in the connection profile', channel_name);
  12. logger.error(message);
  13. throw new Error(message);
  14. }
  15. // 生成基于组织管理员的 TransactionID 对象
  16. // 该对象包含一个随机数(nonce)以及t(nonce + signingIdentity)的hash值
  17. let request = {
  18. txId : client.newTransactionID(true)
  19. };
  20. // 从orderer节点获取创世区块(genesis block)
  21. let genesis_block = await channel.getGenesisBlock(request);
  22. // 告诉节点加入channel,等待每个节点的event hub通知我们节点加入channel成功
  23. var promises = [];
  24. var block_registration_numbers = [];
  25. // 给组织中的每个节点分配一个EventHub对象
  26. let event_hubs = client.getEventHubsForOrg(org_name);
  27. #event_hubs.forEach((eh) => {
  28. #let configBlockPromise = new Promise((resolve, reject) => {
  29. // 设置超时时间
  30. #let event_timeout = setTimeout(() => {
  31. let message = 'REQUEST_TIMEOUT:' + eh._ep._endpoint.addr;
  32. logger.error(message);
  33. eh.disconnect();
  34. reject(new Error(message));
  35. }, 60000);
  36. // 注册区块监听
  37. #let block_registration_number = eh.registerBlockEvent((block) => {
  38. clearTimeout(event_timeout);
  39. // 配置区块中只能有一个交易
  40. if (block.data.data.length === 1) {
  41. // 一个节点可能属于多个channel
  42. // 所以需要检验该区块是否来自目标channel
  43. var channel_header = block.data.data[0].payload.header.channel_header;
  44. if (channel_header.channel_id === channel_name) {
  45. let message = util.format('EventHub %s has reported a block update for channel %s',eh._ep._endpoint.addr,channel_name);
  46. logger.info(message)
  47. resolve(message);
  48. } else {
  49. let message = util.format('Unknown channel block event received from %s',eh._ep._endpoint.addr);
  50. logger.error(message);
  51. reject(new Error(message));
  52. }
  53. }
  54. }, (err) => {
  55. clearTimeout(event_timeout);
  56. let message = 'Problem setting up the event hub :'+ err.toString();
  57. logger.error(message);
  58. reject(new Error(message));
  59. });
  60. // 每一个client实例对应一个注册number,后面将作为参数对监听进行注销
  61. block_registration_numbers.push(block_registration_number);
  62. // 保存EventHub对象,方便后面对事件流断开连接
  63. all_eventhubs.push(eh);
  64. });
  65. promises.push(configBlockPromise);
  66. // 开启事件流
  67. eh.connect();
  68. });
  69. // 封装加入channel的请求
  70. let join_request = {
  71. targets: peers, // 要加入channel的节点
  72. txId: client.newTransactionID(true), // 基于组织管理员的TransactionID
  73. block: genesis_block // 创世区块
  74. };
  75. // 调用SDK中的joinChannel()方法,主要是通过sendProposal()将
  76. // 加入channel的交易提案发送给背书节点进行背书
  77. let join_promise = channel.joinChannel(join_request);
  78. // 保存返回结果:提案响应(ProposalResponse)的Promise
  79. promises.push(join_promise);
  80. let results = await Promise.all(promises);
  81. logger.debug(util.format('Join Channel RESPONSE : %j', results));
  82. // 检查所有Promise返回(包括监听事件和发送join请求)
  83. // 只要有一个结果异常则宣布join channel失败
  84. let peers_results = results.pop();
  85. for(let i in peers_results) {
  86. let peer_result = peers_results[i];
  87. if(peer_result.response && peer_result.response.status == 200) {
  88. logger.info('Successfully joined peer to the channel %s',channel_name);
  89. } else {
  90. let message = util.format('Failed to joined peer to the channel %s',channel_name);
  91. error_message = message;
  92. logger.error(message);
  93. }
  94. }
  95. // 查看事件中心的消息报告
  96. for(let i in results) {
  97. let event_hub_result = results[i];
  98. let event_hub = event_hubs[i];
  99. let block_registration_number = block_registration_numbers[i];
  100. logger.debug('Event results for event hub :%s',event_hub._ep._endpoint.addr);
  101. if(typeof event_hub_result === 'string') {
  102. logger.debug(event_hub_result);
  103. } else {
  104. if(!error_message) error_message = event_hub_result.toString();
  105. logger.debug(event_hub_result.toString());
  106. }
  107. // 注销事件监听
  108. event_hub.unregisterBlockEvent(block_registration_number);
  109. }
  110. } catch(error) {
  111. logger.error('Failed to join channel due to error: ' + error.stack ? error.stack : error);
  112. error_message = error.toString();
  113. }
  114. // 断开所有event hub事件流
  115. all_eventhubs.forEach((eh) => {
  116. eh.disconnect();
  117. });
  118. };

测试

  • 创建channel

    1. curl -s -X POST \
    2. http://localhost:4000/channels \
    3. -H "authorization: Bearer $ORG1_TOKEN" \
    4. -H "content-type: application/json" \
    5. -d '{
    6. "channelName":"mychannel",
    7. "channelConfigPath":"../artifacts/channel/mychannel.tx"
    8. }'
  • 返回结果

    1. {"success":true,"message":"Channel 'mychannel' created Successfully"}
  • 将Org1中的两个peer加入channel

    1. curl -s -X POST \
    2. http://localhost:4000/channels/mychannel/peers \
    3. -H "authorization: Bearer $ORG1_TOKEN" \
    4. -H "content-type: application/json" \
    5. -d '{
    6. "peers": ["peer0.org1.example.com","peer1.org1.example.com"]
    7. }'
  • 返回结果

    1. {"success":true,"message":"Successfully joined peers in organization Org1 to the channel:mychannel"}

总结

创建channel:

  • 生成client对象,从channel配置交易mychannel.tx中提取配置信息
  • 利用组织admin的私钥进行签名,这个过程相当于背书
  • 封装请求结构(包括channel配置信息,对配置信息的签名,channelName,基于admin的txId), 并调用 clinet.createChannel()将请求发送到orderer节点
  • 实际上在内部调用orderer.sendBroadcast(),向order service广播交易,orderer将配置交易打包成区块后广播给各记账节点,记入账本中。(这个配置区块就是创建的channel所对应的chain的创世区块)

加入channel:

  • 生成client对象,生成channel对象
  • orderer节点获取genesis block (调用 orderer.sendDeliver() 接口)
  • 对要加入channel的每个peer设置事件区块监听,根据指定channel是否生成了新的配置区块来判断join channel过程是否成功
  • 封装请求结构(包括 目标peers,基于组织admin的txId,genesis block),调用channel.joinChannel()方法,内部是通过peer.sendProposal()将该交易提案发送给背书节点进行背书,返回背书响应Promise

    客户端将背书过的交易发送到orderer服务,排序后广播给记账节点,将该配置区块记录到ledger中,此时EventHub监听到该事件。
  • 加入channel成功后,注销区块事件监听,断开所有event hub事件流。

Hyperledger Fabric——balance transfer(三)创建和加入Channel的更多相关文章

  1. Hyperledger Fabric——balance transfer(一)启动示例

    Blacne transfer是Hyperledger fabric Node SDK的一个示例应用,主要使用了SDK中fabric-client 和 fabric-ca-client 模块中的API ...

  2. Hyperledger Fabric——balance transfer(六)查询

    balance transfer 提供了很多查询接口,包括链码查询,根据区块号查询区块数据,根据交易ID查询交易信息,查询链上的区块数,查询已安装或已实例化的链码,查询通道. 源码解析 1.调用链码查 ...

  3. Hyperledger Fabric——balance transfer(四)安装和实例化chaincode

    详细解析blance transfer示例的安装(install)和实例化(Instantiate)链码(chaincode)的过程.安装chaincode会根据本地的链码文件生成chaincode镜 ...

  4. Hyperledger Fabric——balance transfer(二)注册用户

    详细分析blance transfer示例的用户注册(register)与登录(enroll)功能. 源码分析 1.首先分析项目根目录的app.js文件中关于用户注册和登录的路由函数.注意这里的tok ...

  5. Hyperledger Fabric——balance transfer(五)执行交易

    链码安装和实例化之后就可以调用chaincode执行交易,下面分析简单的账户转账操作是如何完成的. 源码分析 1.首先看app.js的路由函数 app.post('/channels/:channel ...

  6. Hyperledger Fabric(v1.2.0)代码分析1——channel创建

    Hyperledger Fabric(v1.2.0)代码分析1--channel创建 0. e2e_cli Hyperledger Fabric提供了一个e2e的例子,该例中创建了一个基础的区块链网络 ...

  7. HyperLedger Fabric Introduction——区块链超级账本介绍

    介绍 HyperLedger Fabric是一个基于模块化架构的分布式账本解决方案平台,它拥有深度加密.便捷扩展.部署灵活及可插拔等特性.它设计之初的目的是支持不同组件的可插拔实现,并适应整个经济生态 ...

  8. Hyperledger Fabric介绍

    转载地址 https://blog.csdn.net/xiaonu123/article/details/81006936 简介 Hyperledger介绍 超级账本(Hyperledger)项目是首 ...

  9. 搭建基于hyperledger fabric的联盟社区(三) --生成公私钥证书及配置文件

    一.生成公私钥和证书 Fabric中有两种类型的公私钥和证书,一种是给节点之前通讯安全而准备的TLS证书,另一种是用户登录和权限控制的用户证书.这些证书本来应该是由CA来颁发,但是目前只有两个社区,所 ...

随机推荐

  1. axios的使用小技巧:如何绕过字符串拼接,直接传递对象

     Vue.js官方推荐使用axios作为发送http请求的工具,在使用axios中,有些小技巧是不容易发现的.当我们不知道这些技巧时,我们可能会使用其他"奇技淫巧",比如,我们很容 ...

  2. Nodejs的介绍

    Nodejs的介绍 Node.js的是建立在Chrome的JavaScript的运行时,可方便地构建快速,可扩展的网络应用程序的平台.Node.js使用事件驱动,非阻塞I/O模型,轻量.高效,可以完美 ...

  3. Linked List-1

    链表一直是面试的重点问题,恰好最近看到了Stanford的一篇材料,涵盖了链表的基础知识以及派生的各种问题. 第一篇主要是关于链表的基础知识. 一.基本结构 1.数组回顾 链表和数组都是用来存储一堆数 ...

  4. 图论--2-SAT--HDU/HDOJ 1814 Peaceful Commission

    Peaceful Commission Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  5. 1) drf 整体了解

    一.接口 """ 1.什么是接口:url+请求参数+响应数据 | 接口文档 ​ 2.接口规范: url:https,api,资源(名词复数),v1,get|post表示操 ...

  6. 题目分享R

    题意:有n只蚂蚁在木棍上爬行,每只蚂蚁的速度都是每秒1单位长度,现在给你所有蚂蚁初始的位置(蚂蚁运动方向未定),蚂蚁相遇会掉头反向运动,让你求出所有蚂蚁都·掉下木棍的最短时间和最长时间. 分析:(其实 ...

  7. Element upload组件上传图片与回显图片

    场景:新增商品时需要添加商品主图,新增成功之后可编辑 上传图片: <el-form-item label="专区logo:" style="height:160px ...

  8. Java——TCP/IP超详细总结

    网络的基础知识 一.协议 1.简介: 在计算机网络与信息通信领域里,人们经常提及“协议”一词.互联网中常用的具有代表性的协议有IP.TCP.HTTP等.而LAN(局域网)中常用的协议有IPX/SPX” ...

  9. PHP循环引用会遇到的坑

    今天遇到这样一个问题: 如果foreach循环一个数组,引用去对它的元素做一些操作,会有什么问题吗? 比如 [1, 2, 3],foreach循环的时候,引用给每个元素 * 2,再去foreach输出 ...

  10. 004_Python的列表切片,增删改查,常用操作方法,元组,range,join

    列表 列表是Python中的基础数据类型之一,它是以[]括起来,每个元素以逗号隔开,而且他里面可以存放各种数据类型比如: li = ['kevin',123,True,(1,2,3,'wusir'), ...