1. 程式人生 > >Hyperledger Fabric——balance transfer(三)建立和加入Channel

Hyperledger Fabric——balance transfer(三)建立和加入Channel

詳細解析blance transfer示例的建立通道(Channel)和加入節點到通道的過程。

建立Channel

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

var createChannel = require('./app/create-channel.js');
app.post('/channels', async function(req, res) {
    // 接收引數channel名稱和配置檔案的路徑
    // 通道配置交易路徑: ./artifacts/channel/mychannel.tx
    var channelName = req.body.channelName;
    var
channelConfigPath = req.body.channelConfigPath; if (!channelName) { res.json(getErrorMessage('\'channelName\'')); return; } if (!channelConfigPath) { res.json(getErrorMessage('\'channelConfigPath\'')); return; } // 建立通道(username和orgname引數從token中獲取)
let message = await createChannel.createChannel(channelName, channelConfichannelNamegPath, req.username, req.orgname); res.send(message); });

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

// 通過傳送交易的方法向orderer節點發送建立channel的請求
var createChannel = async function(channelName, channelConfigPath, username, orgName)
{
try { // 生成組織的客戶端例項 var client = await helper.getClientForOrg(orgName); // 從channel配置交易(mychannel.tx)讀取源位元組 var envelope = fs.readFileSync(path.join(__dirname, channelConfigPath)); // 提取channel配置資訊 var channelConfig = client.extractChannelConfig(envelope); // 客戶端使用 組織管理員 的私鑰 簽名,作為背書(endorsement) // "../artifacts/channel/crypto-config/peerOrganizations/org1.example.com/users/[email protected]/msp/signcerts" // 在內部通過_getSigningIdentity(true)獲取org admin的私鑰 進行簽名 let signature = client.signChannelConfig(channelConfig); // 請求結構: let request = { config: channelConfig, signatures: [signature], name: channelName, txId: client.newTransactionID(true) }; // 傳送給orderer節點 var response = await client.createChannel(request) if (response && response.status === 'SUCCESS') { logger.debug('Successfully created the channel.'); let response = { success: true, message: 'Channel \'' + channelName + '\' created Successfully' }; return response; } else { throw new Error('Failed to create the channel \'' + channelName + '\''); } } catch (err) { logger.error('Failed to initialize the channel: ' + err.stack ? err.stack : err); throw new Error('Failed to initialize the channel: ' + err.toString()); } };

加入節點到Channel

1.首先看app.js裡的路由函式

// 首先看app.js中的路由函式
app.post('/channels/:channelName/peers', async function(req, res) {
    // 接收引數 channel名稱(URL中)和peers(body中)
    var channelName = req.params.channelName;
    var peers = req.body.peers;

    if (!channelName) {
        res.json(getErrorMessage('\'channelName\''));
        return;
    }
    if (!peers || peers.length == 0) {
        res.json(getErrorMessage('\'peers\''));
        return;
    }
    // 將peers加入channel
    let message =  await join.joinChannel(channelName, peers, req.username, req.orgname);
    res.send(message);
});

2.然後看join-channel.js中的joinChannel()函式

// 然後看join-channel.js中的joinChannel()函式
var joinChannel = async function(channel_name, peers, username, org_name) {
    var error_message = null;
    var all_eventhubs = [];
    try {
        // 首先建立client物件
        var client = await helper.getClientForOrg(org_name, username);
        //  生成channel物件
        var channel = client.getChannel(channel_name);
        if(!channel) {
            let message = util.format('Channel %s was not defined in the connection profile', channel_name);
            logger.error(message);
            throw new Error(message);
        }

        // 生成基於組織管理員的 TransactionID 物件
        // 該物件包含一個隨機數(nonce)以及t(nonce + signingIdentity)的hash值
        let request = {
            txId :  client.newTransactionID(true) 
        };

        // 從orderer節點獲取創世區塊(genesis block)
        let genesis_block = await channel.getGenesisBlock(request);

        // 告訴節點加入channel,等待每個節點的event hub通知我們節點加入channel成功
        var promises = [];
        var block_registration_numbers = [];
        // 給組織中的每個節點分配一個EventHub物件
        let event_hubs = client.getEventHubsForOrg(org_name);
        #event_hubs.forEach((eh) => {
            #let configBlockPromise = new Promise((resolve, reject) => {
                // 設定超時時間
                #let event_timeout = setTimeout(() => {
                    let message = 'REQUEST_TIMEOUT:' + eh._ep._endpoint.addr;
                    logger.error(message);
                    eh.disconnect();
                    reject(new Error(message));
                }, 60000);
                // 註冊區塊監聽
                #let block_registration_number = eh.registerBlockEvent((block) => {
                    clearTimeout(event_timeout);
                    // 配置區塊中只能有一個交易
                    if (block.data.data.length === 1) {
                        // 一個節點可能屬於多個channel
                        // 所以需要檢驗該區塊是否來自目標channel
                        var channel_header = block.data.data[0].payload.header.channel_header;
                        if (channel_header.channel_id === channel_name) {
                            let message = util.format('EventHub %s has reported a block update for channel %s',eh._ep._endpoint.addr,channel_name);
                            logger.info(message)
                            resolve(message);
                        } else {
                            let message = util.format('Unknown channel block event received from %s',eh._ep._endpoint.addr);
                            logger.error(message);
                            reject(new Error(message));
                        }
                    }
                }, (err) => {
                    clearTimeout(event_timeout);
                    let message = 'Problem setting up the event hub :'+ err.toString();
                    logger.error(message);
                    reject(new Error(message));
                });
                // 每一個client例項對應一個註冊number,後面將作為引數對監聽進行登出
                block_registration_numbers.push(block_registration_number);
                // 儲存EventHub物件,方便後面對事件流斷開連線
                all_eventhubs.push(eh); 
            });
            promises.push(configBlockPromise);
            // 開啟事件流
            eh.connect(); 
        });

        // 封裝加入channel的請求
        let join_request = {
            targets: peers,                             // 要加入channel的節點
            txId: client.newTransactionID(true),        // 基於組織管理員的TransactionID
            block: genesis_block                        // 創世區塊
        };
        // 呼叫SDK中的joinChannel()方法,主要是通過sendProposal()將
        // 加入channel的交易提案發送給背書節點進行背書
        let join_promise = channel.joinChannel(join_request);
        // 儲存返回結果:提案響應(ProposalResponse)的Promise
        promises.push(join_promise);
        let results = await Promise.all(promises);
        logger.debug(util.format('Join Channel RESPONSE : %j', results));

        // 檢查所有Promise返回(包括監聽事件和傳送join請求)
        // 只要有一個結果異常則宣佈join channel失敗
        let peers_results = results.pop();
        for(let i in peers_results) {
            let peer_result = peers_results[i];
            if(peer_result.response && peer_result.response.status == 200) {
                logger.info('Successfully joined peer to the channel %s',channel_name);
            } else {
                let message = util.format('Failed to joined peer to the channel %s',channel_name);
                error_message = message;
                logger.error(message);
            }
        }
        // 檢視事件中心的訊息報告
        for(let i in results) {
            let event_hub_result = results[i];
            let event_hub = event_hubs[i];
            let block_registration_number = block_registration_numbers[i];
            logger.debug('Event results for event hub :%s',event_hub._ep._endpoint.addr);
            if(typeof event_hub_result === 'string') {
                logger.debug(event_hub_result);
            } else {
                if(!error_message) error_message = event_hub_result.toString();
                logger.debug(event_hub_result.toString());
            }
            // 登出事件監聽
            event_hub.unregisterBlockEvent(block_registration_number);
        }
    } catch(error) {
        logger.error('Failed to join channel due to error: ' + error.stack ? error.stack : error);
        error_message = error.toString();
    }

    // 斷開所有event hub事件流
    all_eventhubs.forEach((eh) => {
        eh.disconnect();
    });
};

測試

  • 建立channel

    curl -s -X POST \
      http://localhost:4000/channels \
      -H "authorization: Bearer $ORG1_TOKEN" \
      -H "content-type: application/json" \
      -d '{
        "channelName":"mychannel",
        "channelConfigPath":"../artifacts/channel/mychannel.tx"
    }'
  • 返回結果

    {"success":true,"message":"Channel 'mychannel' created Successfully"}
  • 將Org1中的兩個peer加入channel

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

    {"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

詳細解析blance transfer示例的建立通道(Channel)和加入節點到通道的過程。 建立Channel 1.首先看app.js的路由函式 var createChannel = require('./app/create-cha

Hyperledger Fabric——balance transfer查詢

balance transfer 提供了很多查詢介面,包括鏈碼查詢,根據區塊號查詢區塊資料,根據交易ID查詢交易資訊,查詢鏈上的區塊數,查詢已安裝或已例項化的鏈碼,查詢通道。 原始碼解析

hyperledger fabric 測試 1.13 加入新的組織

搭建好的環境已經可以正常執行多組織多節點的fabric,在已經正常執行的通道中加入新的組織,步驟如下: 1.如果要手動執行,首先在docker-compose-org3.yaml中增加org3,同時在cli和org3中設定手動執行變數CORE_LOGGING_LEVEL為D

HyperLeger Fabric SDK開發——fabsdk

HyperLeger Fabric SDK開發(三)——fabsdk 一、fabsdk簡介 1、fabsdk簡介 fabsdk是Fabric SDK的主要包,fabsdk支援客戶端使用Hyperledger Fabric區塊鏈網路。fabsdk基於配置建立上下文環境,上下文環境會在client包使用。官

我對hyperledger fabric的執著:環境整理

我用的作業系統為:centos7.4,語言準備用java開發。 整理包括:linux核心升級,jdk安裝,docker(docker ce,docker-compose)安裝,go語言環境安裝。 1、Docker要求的linux核心版本最低為3.10,故需將linux核心

討喜的隔離可變性建立角色

宣告:本文是《Java虛擬機器併發程式設計》的第五章,感謝華章出版社授權併發程式設計網站釋出此文,禁止以任何形式轉載此文。 正如前面曾經提到過的那樣,雖然我們有很多支援角色的類庫可供選擇,但是在本書中我們將使用Akka。這是一個基於Scala的類庫,該類庫擁有非常好的效能和可擴充套件性、並同時

執行緒執行者建立一個大小固定的執行緒執行者

宣告:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     譯者:許巧輝     校對:方騰飛,葉磊 建立一個大小固定的執行緒執行者 當你使用由Executors類的 newCachedThreadPo

Windows10下的docker安裝與入門 建立自己的docker映象並且在容器中執行它

Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何介面。 本教程主要分以下幾點內容:  ------------

VSTO學習——建立Excel解決方案

四、建立Excel外接程式介紹完了Excel物件模型之後,我們就可以利用這些物件來對Excel文件進行操作了,下面就建立一個簡單的Excel外接程式的。首先我們模擬一個需求,大多說軟體在使用時都會彈出一個歡迎介面,這樣我們就建立一個外接程式,每次開啟Excel檔案時彈出一個歡迎介面,退出時彈出“謝謝使用”介面

R語言入門——筆記建立資料集

第一部分:資料結構 1.建立向量:c() c(…, recursive = FALSE, use.names = TRUE) 根據元素座標訪問 2.建立矩陣:matrix() matrix(data = NA, nrow = 1, ncol

Machine Learning第六講[應用機器學習的建議] --建立一個垃圾郵件分類器

內容來自Andrew老師課程Machine Learning的第六章內容的Building a Spam Classifier部分。 一、Prioritizing What to Work on(優

windows server 2012 AD 域站點部署系列建立林中第一個域

前一篇已經介紹了路由器的建立,實現在個子網互相連通,本篇博文開始進入域和站點的正式部署,首先介紹建立林中第一域,準備一臺虛擬機器,命名為DC1,部署步驟如下: 一、網路配置: 1、在hyper-v管

C語言網路程式設計建立套接字通訊TCP

為了實現伺服器與客戶機的通訊,伺服器和客戶機都必須建立套接字。伺服器與客戶機的工作原理可以用下面的過程來描述。 (1)伺服器先用socket函式來建立一個套接字,用這個套接字完成通訊的監聽。 (2)用bind函式來繫結一個埠號和IP地址。因為本地計算機可能有多個網絡卡和IP

通過Gazebo模擬學TurtleBot3——建立模擬工程

1. 模擬工程連結地址 2. 建立模擬工程 $ mkdir -p ~/catkin_ws/src $ cd ~/catkin_ws/src $ git clone https://github.com/robinhyg/tb3_gazebo_le

WCF RIA 服務 - 建立一個RIA Services Solution

在本節,我們將進行一個演練。我們建立一個WCF RIA Services 應用,此應用從AdventureWorksLT 資料庫中檢索資料。我們會用SL來建立表現層的客戶端,並建立表現不同資料庫表的實體類。 此節需要: Visual Studio 2008 Sil

C++遞迴遍歷資料夾——建立樹結構

補充上篇部落格:遞迴遍歷資料夾時,同步在記憶體中建立相同的樹狀結構,用來描述所有檔案和資料夾的儲存結構。 具體實現如下: // recursion3.cpp #include <vect

ubuntu server 14.04 編譯安裝xen4.4.2配置vtpm——建立DomUa PV VM

為了建立的半虛擬化VM能上網,在domain0中需設定網橋: #vi /etc/network/interface 修改如下: # The loopback network interfaces auto lo iface lo inet loopback #The p

Android視窗機制WindowWindowManager的建立與Activity

Android視窗機制系列 Android視窗機制(一)初識Android的視

Fabric進階—— 使用SDK動態增加組織

在**fabric**網路執行過程中動態追加新的組織是相當複雜的,網上的資料也十分匱乏,大多是基於**first-network**這樣的簡單示例,而且是使用啟動**cli**容器的方法來增加組織,幾乎沒有針對實際應用的解決方案。本文介紹瞭如何在應用程式中呼叫**SDK**來進行組織的動態增加。 #

C語言---輸入輸出

而是 abs 空間 精度 保存 括號 ger 原型 parameter 一、輸出   輸入輸出:英文是Input/Output簡稱IO。   C語言標準函數庫中,提供了關於以下介紹的幾種輸出函數,(所有標準輸入輸出函數在調用前,必須先包含頭文件stdio.h) 1.1 簡單