Hyperledger04

代碼html

'use strict';


var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');


var fabric_client = new Fabric_Client();

// 設置fabric網絡
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);

//
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

var query =async (fcn,args)=>{
    try {
        // create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
        var state_store = await Fabric_Client.newDefaultKeyValueStore({path: store_path});
        // assign the store to the fabric client
        fabric_client.setStateStore(state_store);
        var crypto_suite = Fabric_Client.newCryptoSuite();
        // use the same location for the state store (where the users' certificate are kept)
        // and the crypto store (where the users' keys are kept)
        var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
        crypto_suite.setCryptoKeyStore(crypto_store);
        fabric_client.setCryptoSuite(crypto_suite);

        // get the enrolled user from persistence, this user will sign all requests
        var user_from_store = await fabric_client.getUserContext('user1', true);

        if (user_from_store && user_from_store.isEnrolled()) {
            console.log('Successfully loaded user1 from persistence');
            member_user = user_from_store;
        } else {
            throw new Error('Failed to get user1.... run registerUser.js');
        }

        // queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
        // queryAllCars chaincode function - requires no arguments , ex: args: [''],
        const request = {
            //targets : --- letting this default to the peers assigned to the channel
            chaincodeId: 'fabcar',
            fcn: fcn,
            args: args
        };

        // send the query proposal to the peer
        var query_responses = await channel.queryByChaincode(request);

        console.log("Query has completed, checking results");
        // query_responses could have more than one  results if there multiple peers were used as targets
        if (query_responses && query_responses.length == 1) {
            if (query_responses[0] instanceof Error) {
                console.error("error from query = ", query_responses[0]);
            } else {
                console.log("Response is ", query_responses[0].toString());
            }
        } else {
            console.log("No payloads were returned from query");
        }
    }catch (err){
        console.error('Failed to query successfully :: ' + err);
    }
};

console.log(process.argv[2]);
console.log(process.argv[3]);
var args = new Array(process.argv[3]);
query(process.argv[2],args);
const express = require('express')
const app = express()


var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');


var fabric_client = new Fabric_Client();

// 設置fabric網絡
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);

//
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

var query =async (fcn,args)=>{
    try {
        // create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
        var state_store = await Fabric_Client.newDefaultKeyValueStore({path: store_path});
        // assign the store to the fabric client
        fabric_client.setStateStore(state_store);
        var crypto_suite = Fabric_Client.newCryptoSuite();
        // use the same location for the state store (where the users' certificate are kept)
        // and the crypto store (where the users' keys are kept)
        var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
        crypto_suite.setCryptoKeyStore(crypto_store);
        fabric_client.setCryptoSuite(crypto_suite);

        // get the enrolled user from persistence, this user will sign all requests
        var user_from_store = await fabric_client.getUserContext('user1', true);

        if (user_from_store && user_from_store.isEnrolled()) {
            console.log('Successfully loaded user1 from persistence');
            member_user = user_from_store;
        } else {
            throw new Error('Failed to get user1.... run registerUser.js');
        }

        // queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
        // queryAllCars chaincode function - requires no arguments , ex: args: [''],
        const request = {
            //targets : --- letting this default to the peers assigned to the channel
            chaincodeId: 'fabcar',
            fcn: fcn,
            args: args
        };

        // send the query proposal to the peer
        var query_responses = await channel.queryByChaincode(request);

        console.log("Query has completed, checking results");
        // query_responses could have more than one  results if there multiple peers were used as targets
        if (query_responses && query_responses.length == 1) {
            if (query_responses[0] instanceof Error) {
                return ("error from query = ", query_responses[0]);
            } else {
                return("Response is ", query_responses[0].toString());
            }
        } else {
            return("No payloads were returned from query");
        }
    }catch (err){
        return('Failed to query successfully :: ' + err);
    }
};

app.get('/:fcn/:fcn1',async (req, res) =>{

    console.log(req.params.fcn);
    console.log(req.params.fcn1);
    var result = await query(req.params.fcn,new Array(req.params.fcn1));
    res.send('Hello World!'+ result);

});

app.listen(80, () => console.log('Example app listening on port 80!'))

概念回顧

hyperledger fabirc的三個重要角色前端

  • client
    客戶端,用來發起transaction propose(提案), 能夠是cli, node sdk或者java sdk
  • peers
    最多見的節點,維護了ledger的副本. 記錄,驗證,同步數據.
  • orderer
    接收背書後的請求,排序,生成區塊,最後交給peer節點.

共識的達成

fabirc的共識達成經過三個步驟java

  1. 客戶端發起提案,每一個peer節點模擬執行,進行背書
  2. orderer節點進行排序
  3. orderer節點驗證後生成區塊交給peer節點去apply

三個步驟保證了區塊鏈數據的一致性和正確性node

Transaction 流程(一)


endorsing peer其實是一些特殊的peer. 由於不是每一個節點都會參與背書. 根據背書策略指定.react

Transaction流程(二)

在智能合約實例化的時候,咱們指定了背書策略, 每一個peer節點模擬執行的結果會反饋給sdk, sdk收集到節點模擬執行的讀寫集後,根據背書策略來決定是不是合法請求.
不一樣的channel能夠有不一樣chaincode,不一樣的chaincode有不一樣的背書策略.ios

Transaction流程(三)

client提交背書後的讀寫集(RW Sets) 給orderer節點. 注意: 在同一時間可能會有不一樣的client提交背書後的讀寫集, 這個提交操做是並行的.
git

Transaction流程(四)

orderer節點要驗證讀寫集,排序,生成區塊,最終把區塊交給全部的peer節點,讓他們更新ledger數據
github

orderer節點

區塊鏈須要解決雙花問題(double speding), 解決雙花就是要把並行的事情,最終變成線性, 把可能引起不一致的並行操做進行串行化. 以賣火車票爲例, 同一張票同一個座位有可能會被兩個不一樣的代售點同時賣出. 解決思路有多種, 賣票前先打電話詢問其餘的售票點,確認不衝突才能夠賣,這就是同步鎖的方式, 或者約定了第一家售票點只能在8點-9點賣票,第二家售票點只能在9點-10點賣票.這是經過令牌方式解決, 另外一種方式就是全部出票操做交給一箇中心的機構進行出票, 中心出票以前會檢查是否還有票,沒有票了就出票失敗...
hyperledger fabirc的 orderer節點就是採用了相似中心機構出票的方式. 因此他效率很高, 沒有挖礦的概念.面試

orderer的排序機制

  • solo 單一orderer節點用的玩具級別的排序服務,單一orderer服務器,採用solo方式
  • kafka 阿帕奇的開源流式消息處理服務平臺. 提供非拜占庭錯誤(故障錯誤)的容錯性.
  • SBFT 簡單拜占庭容錯, 容忍集羣中的orderer節點有不超過1/3的錯誤. 目前還在實現中...

在開發者的角度,orderer採用什麼排序方法,對開發人員來說是透明的. 代碼都是同樣的.只是修改一個配置.算法

Transaction流程(五)


committing peer驗證讀寫集跟當前的世界狀態是否一致. 一致的話就會更新ledger, 世界狀態會跟隨變化, 若是不一致不會更新ledger,但transaction仍是會被記錄.世界狀態不會發生變化.

Transaction流程(六)


最後,committing peers會異步的通知client, transaction是成功仍是失敗. 咱們監聽這個回調,就能夠知道數據是否被寫入成功.

以上流程須要理解,記憶! 面試須要能說出來.

channels

channel至關於hyperledger fabirc的子網絡, 不一樣的channel裏面的內容彼此獨立,徹底隔離.
經過channel能夠保證區塊鏈參與者的隱私和數據隔離.
不一樣的channel,擁有不一樣的application, 不一樣的ledger,不一樣的peers

  • 一個peer能夠屬於多個channel
  • 一個channel能夠擁有多個peer
  • 不一樣chanel裏面的數據彼此隔離
  • orderer能夠看到全部channel的數據

state db, 狀態數據庫

世界狀態被存儲在狀態數據庫裏面
chaincode執行後stub.putState(key, Buffer.from(value)),
這些信息都是被key,value的形式存放到狀態數據庫中
經過stub.getState(key)的方式讀出來

hyperledger fabric 支持兩種模式的狀態數據庫

  • levelDB 文件形式存儲, 不易查看管理.
  • couchDB 支持福查詢,獨立的容器數據庫

智能合約(鏈碼)

hyperledger fabric的智能合約叫chaincode鏈碼, 能夠用nodejs或者是go語言編寫,
chaincode就是咱們的business logic, 任何更新ledger的操做都只能經過智能合約來完成.

MSP

hyperledger fabric是一個受權網絡, 成員管理服務提供者是一個組件,用於定義身份,驗證身份和容許訪問網絡的規則。 MSP使用CA來頒發證書,MSP的默認接口是Fabric-CA API

Fabirc- CA

頒發或者召回 用戶的證書, 用戶只有持有證書才能夠加入fabirc網絡,進行轉帳操做.

項目實戰

開發環境搭建

  1. docker
  2. docker-compose
  3. git
  4. nodejs
#安裝docker
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
#安裝docker-compose
curl -L https://github.com/docker/compose/releases/download/1.20.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
#安裝git
apt-get update
apt-get install git
#安裝nodejs
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

修復阿里雲超時bug
/etc/resolv.conf 註釋掉 options timeout:2 attempts:3 rotate single-request-reopen

下載二進制腳本和安裝docker鏡像
curl -sSL https://raw.githubusercontent.com/itheima1/BlockChain/master/tools/bootstrap.sh | bash -s 1.1.0

chaincode實戰

chaincode就是智能合約, 經過編寫純函數的代碼,更新ledger的狀態.
https://fabric-shim.github.io/ChaincodeInterface.html

<async> Init(stub)
Called during chaincode instantiate and upgrade. This is where you will initialize any application state.
智能合約被初始化或者升級的時候調用, 通常用來初始化應用程序的狀態
<async> Invoke(stub)
called throughout the life time of the chaincode to carry out business transaction logic and effect the asset states
商業邏輯, 修改資產的狀態.

智能合約結構

const Chaincode = class {
    async Init(stub) {  // 初始化方法
         await stub.putState(key, Buffer.from(aStringValue)); //能夠初始化一些世界狀態
         return shim.success(Buffer.from('Initialized Successfully!'));
    }
 
    async Invoke(stub) {
       let ret = stub.getFunctionAndParameters(); //獲取函數名和參數
       console.info(ret);
       let method = this[ret.fcn]; //函數
       let payload = await method(stub, ret.params); //調用函數
       return shim.success(payload);
    }

    async xxx(stub, args) {//示例函數
        return "xxx";
    }
};
shim.start(new Chaincode());

項目實戰 --- 區塊鏈項目實戰

區塊鏈不是萬金油, 任何技術都要評估風險和收益. 區塊鏈的特色是數據的可信和不可篡改, 企業作業務應該是使用區塊鏈技術來提高自身業務的健壯程度和抗風險能力. 不該該爲了區塊鏈而區塊鏈, 若是爲了區塊鏈而修改自身業務這種作法是不可取的.

企業項目對區塊鏈的態度應該是,上鍊了有好處, 不想上鍊了,但是隨時下來.

區塊鏈技術不該該跟業務綁定在一塊兒, 數據和業務分析是咱們企業級開發的最佳實戰. 是目前主流的作法和思想.

區塊鏈的做用是,在聯盟中記錄你們都承認,有價值的數據. 經過智能合約去添加和更新數據.

區塊鏈項目分析(一)

雄安新區區塊鏈租房項目
目標:房主租的安心、租客住的放心
租房業務核心數據上鍊

  1. 租房合同信息上鍊
    當房主與租客簽定完合同後,合同圖片信息會上傳到服務器,服務器會對圖片進行SHA256處理,處理結果會記錄
    到區塊中。當產生糾紛時按照合同約定,一旦一方的合同篡改了,其SHA256數據是沒法與區塊鏈中記錄的數據相
    匹配的。
  2. 交易信息上鍊
    針對租金、押金、違約金等交易信息作記錄,項目中記錄:訂單、交易雙方、交易金額、起止時間、備註信息。

之後能夠根據區塊鏈交易信息查詢房東或者租客的信用程度,進行信用評級.

var rentHouse = {
      docHash: 'bf27373a381224a19c3a2886cd896d550ad440dddbfc49f09b3c025b50c56107',
      orderid: '0001',
      houseowner: '周扒皮',
      renter: '楊白勞',
      money: 30000,
      beginDate:'2018-01-01',
      endDate:'2018-12-31',
      note:'年付'
    };

    await stub.putState(args[0], Buffer.from(JSON.stringify(rentHouse)));

區塊鏈項目分析(二)

區塊鏈二手車交易項目
目標:安心的登記,安心的二手車交易
二手車交易核心數據上鍊
車輛交易信息上鍊
針對汽車類型,製造商,型號,顏色,全部人等交易信息作記錄。

之後能夠追蹤汽車交易的整個歷史.

var car = {
     docType: 'car',
      make: '保時捷',
      model: '911',
      color: '冰川白',
      owner: '李天一'
    };

    await stub.putState(args[0], Buffer.from(JSON.stringify(car)));

區塊鏈項目分析(三)

區塊鏈p2p金融借款反欺詐系統
傳統p2p小貸公司, 會對客戶作背景調查,調研客戶的還款能力,收入水平, 但這種報告每每具備很大的侷限性.
區塊鏈技術共享用戶的資產和借貸報告,聯盟鏈各機構之間的溝通成本就降得很低,而且這個項目能夠做爲當前小貸公司的一個加強的外圍功能使用,不會對企業當前的核心業務進行入侵式的影響. 利用區塊鏈的零知識證實等特性, 能夠極大程度的保證用戶的隱私.

例如用戶王思聰以買房資金週轉的名義從建設銀行借款1個億. 通過建設銀行評估, 認爲王思聰在貸款期限內有1億的還款能力,
但是與此同時,王思聰以一樣的理由在工商銀行和農業銀行貸款1個億. 工商銀行和農業銀行也都對王思聰的還款能力進行了評估. 因爲銀行系統放款信息同步不及時,不對稱. 王思聰極可能同時從三個銀行分別貸出來1個億. 那麼這三家銀行極可能其中一家或者兩家會出現同時逾貸的狀況.若是有大量借款人出現上訴狀況, 甚至會致使一家銀行會倒閉破產.

hyperledger fabric 存儲全部人的借貸信息.

uid: 'bf27373a381224a19c3a2886cd896d550ad440dddbfc49f09b3c025b50c56107',
  var loan = {
     timestamp: '1504054225', 
      loanmoney: '1000000',
      applydate: '2018-08-14',
      startdate: '2018-08-15',
      enddate:'2019-08-15',
      realenddate:'0000-00-00'
    };

    await stub.putState(uid, Buffer.from(JSON.stringify(loan)));

注意:以上區塊鏈系統存儲的數據是脫敏後的數據, 用戶的uid是根據用戶的身份證號碼,姓名和公安部提供的指紋特徵碼生成的sha256字符串, 只有獲得借款當事人受權,才能夠拿到這些信息生成uid. 普通機構即便看到這些數據, 也不知道這些數據對應的真實人的身份是什麼.

區塊鏈項目分析(四)

小黃魚項目(案例背景, 你們自行補充)

var fish = {
 Vessel: "奮進號38A", 
 Location: "67.0006, -70.5476", 
 Timestamp: "1504054225",
 Holder: "王大壯"
};
 await stub.putState(id, Buffer.from(JSON.stringify(fish)));

區塊鏈項目分析(五)

航空公司企業積分通用項目

var credits = {
 userid: "bf27373a381224a19c3a2886cd896d550ad440dddbfc49f09b3c025b50c56107", 
 shop: "南航", 
 Timestamp: "1504054225",
 credits: 1668
};
 await stub.putState(id, Buffer.from(JSON.stringify(fish)));

區塊鏈項目分析(六)

物流, 冷鏈溯源, 一帶一路, 電子發票, 跨境支付,數字資產,信息共享,簽證證實...

工做職責

  1. 負責hyperledger chaincode代碼編寫;
  2. 負責協助前端跑通業務邏輯, 業務代碼的修改;
  3. 部分後端nodejs中間件業務代碼的編寫;
  4. 參與與其業務相關的需求變動評審;
  5. 負責hyperledger fabirc技術的研發和內部培訓;
  6. 參與區塊鏈相關業務原型設計;
  7. 負責彙報區塊鏈技術demo方案,給客戶原理講解,效果演示.

技能描述

熟悉區塊鏈技術,熟悉Hyperledger Fabric V1.1超級帳本的實現原理、基本架構分析,鏈碼智能合約實現,共享帳本如何存儲,coachdb, 共識機制如何實現和安全隱私等相關加密算法。熟悉從架構師角度分析設計系統,及程序角度實現系統。
1)瞭解Docker容器虛擬化技術使用;
2)熟悉nodejs語言編寫Chaincode,熟悉Nodejs編寫 SDK。
3)熟悉Liunx系統,熟練Fabric V1.1環境搭建,cli容器調試等。
4)熟悉多主機Peer系統部署,熟悉單主機開發調試,如4+1+2(4個Peer + 1 Orderer+2CA);
5)熟悉分佈式超媒體分發協議-IPFS在區塊鏈中應用。
6)已經實現將業務封裝爲服務,只要調用封裝的服務,就能夠實現全部業務。
7)整理了大量文檔和部署腳本。

農牧廳漁業管理系統實戰

定義資產的json格式

var fish = {
 vessel: "奮進號38A", 
 location: "67.0006, -70.5476", 
 timestamp: "1504054225",
 holder: "王大壯"
};

來到chaincode文件夾 建立fishcc, 智能合約

'use strict';
const shim = require('fabric-shim');
const util = require('util');

let Chaincode = class {
 //初始化智能合約的方法
  async Init(stub) {
    console.info('=========== Instantiated fabcar chaincode ===========');
    return shim.success();
  }
shim.start(new Chaincode());

invode函數設計

init函數

async Invoke(stub) {
    let ret = stub.getFunctionAndParameters(); //獲取函數和參數
    console.info(ret);

    let method = this[ret.fcn];
    if (!method) {
      console.error('找不到要調用的函數,函數名:' + ret.fcn);
      throw new Error('找不到要調用的函數,函數名:' + ret.fcn);
    }
    try {
      let payload = await method(stub, ret.params); //直接調用函數,獲取返回值
      return shim.success(payload);
    } catch (err) {
      console.log(err);
      return shim.error(err);
    }
  }

queryFish函數

async queryFish(stub, args) {
    if (args.length != 1) {
      throw new Error('錯誤的調用參數. 實例: FISH01');
    }
    let fishNumber = args[0];

    let fishAsBytes = await stub.getState(fishNumber); //從帳本中獲取fish的信息,帳本是二進制存儲的
    if (!fishAsBytes || fishAsBytes.toString().length <= 0) {
      throw new Error(fishAsBytes + ' 不存在: ');
    }
    console.log(fishAsBytes.toString());
    return fishAsBytes;
  }

initLedger方法

// 根據官方建議,最好的初始化區塊鏈帳本的方法是單獨編寫一個intLedger的方法.

async initLedger(stub, args) {
    console.info('============= 開始 : 初始化帳本 ===========');
    let fishes = [];
    fishes.push({
       vessel: "奮進號38A", 
       location: "67.0006, -70.5476", 
       timestamp: "1504054225",
       holder: "王大壯"
    });
    fishes.push({
       vessel: "光明號66B", 
       location: "57.9006, -78.3478", 
       timestamp: "1504054666",
       holder: "高大壯"
    });
    fishes.push({
       vessel: "釣魚島58B", 
       location: "77.9034, -75.3455", 
       timestamp: "1504054888",
       holder: "劉胡蘭"
    });
    
    for (let i = 0; i < fishes.length; i++) {
      await stub.putState('FISH' + i, Buffer.from(JSON.stringify(fishes[i])));
      console.info('Added <--> ',fishes[i]);
    }
    console.info('============= 結束 :初始化帳本 ===========');
  }

recordFish方法

async recordFish(stub, args) {
    console.info('============= START : record fish ===========');
    if (args.length != 5) {
      throw new Error('須要5個參數,第0個參數是id,後面的4個參數,   vessel, location,  timestamp, holder');
    }

    var fish = {
      vessel: args[1],
      location: args[2],
      timestamp: args[3],
      holder: args[4]
    };

    await stub.putState(args[0], Buffer.from(JSON.stringify(fish)));
    console.info('============= END : record fish ===========');
  }

queryAllFish方法

async queryAllFish(stub, args) {

    let startKey = 'FISH0';
    let endKey = 'FISH999';

    let iterator = await stub.getStateByRange(startKey, endKey);

    let allResults = [];
    while (true) {
      let res = await iterator.next();

      if (res.value && res.value.value.toString()) {
        let jsonRes = {};
        console.log(res.value.value.toString('utf8'));

        jsonRes.Key = res.value.key;
        try {
          jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
        } catch (err) {
          console.log(err);
          jsonRes.Record = res.value.value.toString('utf8');
        }
        allResults.push(jsonRes);
      }
      if (res.done) {
        console.log('end of data');
        await iterator.close();
        console.info(allResults);
        return Buffer.from(JSON.stringify(allResults));
      }
    }
  }

changeFishHolder方法

更改小黃魚的歸屬人

async changeFishHolder(stub, args) {
    console.info('============= START : changeFishHolder ===========');
    if (args.length != 2) {
      throw new Error('參數數量錯誤,須要兩個參數');
    }

    let fishAsBytes = await stub.getState(args[0]);
    let fish = JSON.parse(fishAsBytes);
    fish.holder = args[1];

    await stub.putState(args[0], Buffer.from(JSON.stringify(fish)));
    console.info('============= END : changeFishHolder ===========');
  }

總結

農牧廳漁業管理的智能合約咱們寫完了, 你們要可以觸類旁通, 其餘業務需求基本上也是相似的模版代碼.
做業: 你們本身實現上面分析的其餘需求.編寫智能合約.

搭建環境,開發區塊鏈App

配置msp信息crypto-config.yaml

OrdererOrgs:

  - Name: Orderer
    Domain: example.com

    Specs:
      - Hostname: orderer

PeerOrgs:

  - Name: Org1
    Domain: org1.example.com

    Template:
      Count: 1

    Users:
      Count: 1

配置組織和排序節點 configtx.yaml

Organizations:
    - &OrdererOrg
        Name: OrdererOrg
        ID: OrdererMSP
        MSPDir: crypto-config/ordererOrganizations/example.com/msp
    - &Org1
        Name: Org1MSP
        ID: Org1MSP
        MSPDir: crypto-config/peerOrganizations/org1.example.com/msp

Application: &ApplicationDefaults
    Organizations:

Orderer: &OrdererDefaults
    OrdererType: solo
    Addresses:
        - orderer.example.com:7050
    BatchTimeout: 2s
    BatchSize:
        MaxMessageCount: 10
        AbsoluteMaxBytes: 99 MB
        PreferredMaxBytes: 512 KB
    Organizations:
Profiles:
    OneOrgOrdererGenesis:
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
    OneOrgChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1

編寫docker-compose.yml配置文件

注意ca的默認密碼是adminpw

version: '2'

networks:
  basic:

services:
  ca.example.com:
    image: hyperledger/fabric-ca
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_CA_NAME=ca.example.com
      - FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
      - FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/4239aa0dcd76daeeb8ba0cda701851d14504d31aad1b2ddddbac6a57365e497c_sk
    ports:
      - "7054:7054"
    command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
    volumes:
      - ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
    container_name: ca.example.com
    networks:
      - basic

  orderer.example.com:
    container_name: orderer.example.com
    image: hyperledger/fabric-orderer
    environment:
      - ORDERER_GENERAL_LOGLEVEL=debug
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - ORDERER_GENERAL_GENESISMETHOD=file
      - ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
      - ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
    command: orderer
    ports:
      - 7050:7050
    volumes:
        - ./config/:/etc/hyperledger/configtx
        - ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/:/etc/hyperledger/msp/orderer
        - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/msp/peerOrg1
    networks:
      - basic

  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    image: hyperledger/fabric-peer
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_PEER_ID=peer0.org1.example.com
      - CORE_LOGGING_PEER=debug
      - CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      # # the following setting starts chaincode containers on the same
      # # bridge network as the peers
      # # https://docs.docker.com/compose/networking/
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    command: peer node start
    # command: peer node start --peer-chaincodedev=true
    ports:
      - 7051:7051
      - 7053:7053
    volumes:
        - /var/run/:/host/var/run/
        - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/msp/peer
        - ./crypto-config/peerOrganizations/org1.example.com/users:/etc/hyperledger/msp/users
        - ./config:/etc/hyperledger/configtx
    depends_on:
      - orderer.example.com
    networks:
      - basic


  cli:
    container_name: cli
    image: hyperledger/fabric-tools
    tty: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
      - CORE_CHAINCODE_KEEPALIVE=10
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
    networks:
        - basic

編寫啓動腳本 start.sh

#!/bin/bash

set -ev

# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1

docker-compose -f docker-compose.yml down

docker-compose -f docker-compose.yml up -d ca.example.com orderer.example.com peer0.org1.example.com 

export FABRIC_START_TIMEOUT=30
sleep ${FABRIC_START_TIMEOUT}

# Create the channel
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.tx
# Join peer0.org1.example.com to the channel.
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block

編寫startFabirc.sh腳本

#!/bin/bash
set -e
# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1
starttime=$(date +%s)
LANGUAGE=node
CC_SRC_PATH=/opt/gopath/src/github.com/fabcar/node
# clean the keystore
rm -rf ./hfc-key-store
# launch network; create channel and join peer to channel
cd ../basic-network
./start.sh
# Now launch the CLI container in order to install, instantiate chaincode
docker-compose -f ./docker-compose.yml up -d cli
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode install -n fishcc -v 1.0 -p "$CC_SRC_PATH" -l node
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n fishcc -l node -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
sleep 20
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" cli peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n fishcc -c '{"function":"initLedger","Args":[""]}'
printf "\n腳本啓動時間 : $(($(date +%s) - starttime)) 秒 ...\n\n\n"
printf "執行'npm install' 安裝依賴文件\n"
printf "執行 'node enrollAdmin.js', 建立管理員, 而後執行 'node registerUser'建立用戶\n\n"
printf "執行 'node invoke.js' 調用函數\n"
printf "執行 'node query.js' 查詢記錄\n\n"

初始化nodejs項目

建立文件夾fabircfish
npm install --save fabric-ca-client
npm install --save fabirc-client
npm install --save grpc

使用node sdk編寫註冊admin的邏輯.獲取admin的證書信息

'use strict';
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
 * Enroll the admin user
 */

var Fabric_Client = require('fabric-client');
var Fabric_CA_Client = require('fabric-ca-client');

var path = require('path');
var util = require('util');
var os = require('os');

//
var fabric_client = new Fabric_Client();
var fabric_ca_client = null;
var admin_user = null;
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log(' Store path:'+store_path);

// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
    // assign the store to the fabric client
    fabric_client.setStateStore(state_store);
    var crypto_suite = Fabric_Client.newCryptoSuite();
    // use the same location for the state store (where the users' certificate are kept)
    // and the crypto store (where the users' keys are kept)
    var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
    crypto_suite.setCryptoKeyStore(crypto_store);
    fabric_client.setCryptoSuite(crypto_suite);
    var tlsOptions = {
        trustedRoots: [],
        verify: false
    };
    // be sure to change the http to https when the CA is running TLS enabled
    fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', tlsOptions , 'ca.example.com', crypto_suite);

    // first check to see if the admin is already enrolled
    return fabric_client.getUserContext('admin', true);
}).then((user_from_store) => {
    if (user_from_store && user_from_store.isEnrolled()) {
        console.log('Successfully loaded admin from persistence');
        admin_user = user_from_store;
        return null;
    } else {
        // need to enroll it with CA server
        return fabric_ca_client.enroll({
          enrollmentID: 'admin',
          enrollmentSecret: 'adminpw'
        }).then((enrollment) => {
          console.log('Successfully enrolled admin user "admin"');
          return fabric_client.createUser(
              {username: 'admin',
                  mspid: 'Org1MSP',
                  cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
              });
        }).then((user) => {
          admin_user = user;
          return fabric_client.setUserContext(admin_user);
        }).catch((err) => {
          console.error('Failed to enroll and persist admin. Error: ' + err.stack ? err.stack : err);
          throw new Error('Failed to enroll admin');
        });
    }
}).then(() => {
    console.log('Assigned the admin user to the fabric client ::' + admin_user.toString());
}).catch((err) => {
    console.error('Failed to enroll admin: ' + err);
});

使用node sdk編寫註冊user1的邏輯.獲取user1的證書信息

'use strict';
/*
* Copyright IBM Corp All Rights Reserved
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
 * Register and Enroll a user
 */

var Fabric_Client = require('fabric-client');
var Fabric_CA_Client = require('fabric-ca-client');

var path = require('path');
var util = require('util');
var os = require('os');

//
var fabric_client = new Fabric_Client();
var fabric_ca_client = null;
var admin_user = null;
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log(' Store path:'+store_path);

// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
    // assign the store to the fabric client
    fabric_client.setStateStore(state_store);
    var crypto_suite = Fabric_Client.newCryptoSuite();
    // use the same location for the state store (where the users' certificate are kept)
    // and the crypto store (where the users' keys are kept)
    var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
    crypto_suite.setCryptoKeyStore(crypto_store);
    fabric_client.setCryptoSuite(crypto_suite);
    var tlsOptions = {
        trustedRoots: [],
        verify: false
    };
    // be sure to change the http to https when the CA is running TLS enabled
    fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', null , '', crypto_suite);

    // first check to see if the admin is already enrolled
    return fabric_client.getUserContext('admin', true);
}).then((user_from_store) => {
    if (user_from_store && user_from_store.isEnrolled()) {
        console.log('Successfully loaded admin from persistence');
        admin_user = user_from_store;
    } else {
        throw new Error('Failed to get admin.... run enrollAdmin.js');
    }

    // at this point we should have the admin user
    // first need to register the user with the CA server
    return fabric_ca_client.register({enrollmentID: 'user1', affiliation: 'org1.department1',role: 'client'}, admin_user);
}).then((secret) => {
    // next we need to enroll the user with CA server
    console.log('Successfully registered user1 - secret:'+ secret);

    return fabric_ca_client.enroll({enrollmentID: 'user1', enrollmentSecret: secret});
}).then((enrollment) => {
  console.log('Successfully enrolled member user "user1" ');
  return fabric_client.createUser(
     {username: 'user1',
     mspid: 'Org1MSP',
     cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
     });
}).then((user) => {
     member_user = user;

     return fabric_client.setUserContext(member_user);
}).then(()=>{
     console.log('User1 was successfully registered and enrolled and is ready to interact with the fabric network');

}).catch((err) => {
    console.error('Failed to register: ' + err);
    if(err.toString().indexOf('Authorization') > -1) {
        console.error('Authorization failures may be caused by having admin credentials from a previous CA instance.\n' +
        'Try again after deleting the contents of the store directory '+store_path);
    }
});

query hyperledger fabirc的信息

'use strict';


var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');


var fabric_client = new Fabric_Client();

// 設置fabric網絡
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);

//
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

var query =async (fcn,args)=>{
    try {
        // create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
        var state_store = await Fabric_Client.newDefaultKeyValueStore({path: store_path});
        // assign the store to the fabric client
        fabric_client.setStateStore(state_store);
        var crypto_suite = Fabric_Client.newCryptoSuite();
        // use the same location for the state store (where the users' certificate are kept)
        // and the crypto store (where the users' keys are kept)
        var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
        crypto_suite.setCryptoKeyStore(crypto_store);
        fabric_client.setCryptoSuite(crypto_suite);

        // get the enrolled user from persistence, this user will sign all requests
        var user_from_store = await fabric_client.getUserContext('user1', true);

        if (user_from_store && user_from_store.isEnrolled()) {
            console.log('Successfully loaded user1 from persistence');
            member_user = user_from_store;
        } else {
            throw new Error('Failed to get user1.... run registerUser.js');
        }

        // queryCar chaincode function - requires 1 argument, ex: args: ['FISH0'],
        // queryAllCars chaincode function - requires no arguments , ex: args: [''],
        const request = {
            //targets : --- letting this default to the peers assigned to the channel
            chaincodeId: 'fishcc',
            fcn: fcn,
            args: args
        };

        // send the query proposal to the peer
        var query_responses = await channel.queryByChaincode(request);

        console.log("Query has completed, checking results");
        // query_responses could have more than one  results if there multiple peers were used as targets
        if (query_responses && query_responses.length == 1) {
            if (query_responses[0] instanceof Error) {
                console.error("error from query = ", query_responses[0]);
            } else {
                console.log("Response is ", query_responses[0].toString());
            }
        } else {
            console.log("No payloads were returned from query");
        }
    }catch (err){
        console.error('Failed to query successfully :: ' + err);
    }
};


console.log(process.argv[2]);
console.log(process.argv[3]);
var args = new Array(process.argv[3]);
query(process.argv[2],args);

前端獲取數據

import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
    state = {items: []};
    async componentDidMount() {
        var result = await axios.get('http://47.254.69.107/queryAllFish/a');
        console.log(result.data);
        this.setState({
           items:result.data
        });
    }
  render() {
    var htmlbody=[];
    this.state.items.forEach((value)=>{
        htmlbody.push(<h3>{value.Key}, {value.Record.holder}</h3>)
    });
    return (
      <div >
          {htmlbody}
      </div>
    );
  }

添加捕魚記錄

'use strict';

var Fabric_Client = require('fabric-client');
var path = require('path');
var util = require('util');
var os = require('os');

//
var fabric_client = new Fabric_Client();

// 設置 fabric 網絡
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);
var order = fabric_client.newOrderer('grpc://localhost:7050');
channel.addOrderer(order);

//
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;

Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
    // assign the store to the fabric client
    fabric_client.setStateStore(state_store);
    var crypto_suite = Fabric_Client.newCryptoSuite();
    // use the same location for the state store (where the users' certificate are kept)
    // and the crypto store (where the users' keys are kept)
    var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
    crypto_suite.setCryptoKeyStore(crypto_store);
    fabric_client.setCryptoSuite(crypto_suite);

    // get the enrolled user from persistence, this user will sign all requests
    return fabric_client.getUserContext('user1', true);
}).then((user_from_store) => {
    if (user_from_store && user_from_store.isEnrolled()) {
        console.log('Successfully loaded user1 from persistence');
        member_user = user_from_store;
    } else {
        throw new Error('Failed to get user1.... run registerUser.js');
    }

    // get a transaction id object based on the current user assigned to fabric client
    tx_id = fabric_client.newTransactionID();
    console.log("Assigning transaction_id: ", tx_id._transaction_id);

    // recordFish chaincode function - requires 5 args, ex: args: ['FISH5',  "奮進號38A", "67.0006, -70.5476", "1504054225", "王大壯"],
    // changeFishHolder chaincode function - requires 2 args , ex: args: ['FISH5', 'Dave'],
    // must send the proposal to endorsing peers
    var request = {
        //targets: let default to the peer assigned to the client
        chaincodeId: 'fishcc',
        fcn: '',
        args: [''],
        chainId: 'mychannel',
        txId: tx_id
    };

    //1. 發送背書請求到全部的節點
    return channel.sendTransactionProposal(request);
}).then((results) => {
    var proposalResponses = results[0];
    var proposal = results[1];
    let isProposalGood = false;
    if (proposalResponses && proposalResponses[0].response &&
        proposalResponses[0].response.status === 200) {
            isProposalGood = true;
            console.log('Transaction proposal was good');
        } else {
            console.error('Transaction proposal was bad');
        }
    if (isProposalGood) {
        console.log(util.format(
            'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s"',
            proposalResponses[0].response.status, proposalResponses[0].response.message));

        // 根據背書請求建立request對象
        var request = {
            proposalResponses: proposalResponses,
            proposal: proposal
        };

        // 設置30秒的監聽器, 看request請求是否完成
        var transaction_id_string = tx_id.getTransactionID(); //Get the transaction ID string to be used by the event processing
        var promises = [];

        var sendPromise = channel.sendTransaction(request);
        promises.push(sendPromise); //we want the send transaction first, so that we know where to check status

        // get an eventhub once the fabric client has a user assigned. The user
        // is required bacause the event registration must be signed
        let event_hub = channel.newChannelEventHub(peer);

        // using resolve the promise so that result status may be processed
        // under the then clause rather than having the catch clause process
        // the status
        let txPromise = new Promise((resolve, reject) => {
            let handle = setTimeout(() => {
                event_hub.unregisterTxEvent(transaction_id_string);
                event_hub.disconnect();
                resolve({event_status : 'TIMEOUT'}); //we could use reject(new Error('Trnasaction did not complete within 30 seconds'));
            }, 3000);
            event_hub.registerTxEvent(transaction_id_string, (tx, code) => {
                // this is the callback for transaction event status
                // first some clean up of event listener
                clearTimeout(handle);

                // now let the application know what happened
                var return_status = {event_status : code, tx_id : transaction_id_string};
                if (code !== 'VALID') {
                    console.error('The transaction was invalid, code = ' + code);
                    resolve(return_status); // we could use reject(new Error('Problem with the tranaction, event status ::'+code));
                } else {
                    console.log('The transaction has been committed on peer ' + event_hub.getPeerAddr());
                    resolve(return_status);
                }
            }, (err) => {
                //this is the callback if something goes wrong with the event registration or processing
                reject(new Error('There was a problem with the eventhub ::'+err));
            },
                {disconnect: true} //disconnect when complete
            );
            event_hub.connect();

        });
        promises.push(txPromise);

        return Promise.all(promises);
    } else {
        console.error('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...');
        throw new Error('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...');
    }
}).then((results) => {
    console.log('Send transaction promise and event listener promise have completed');
    // check the results in the order the promises were added to the promise all list
    if (results && results[0] && results[0].status === 'SUCCESS') {
        console.log('Successfully sent transaction to the orderer.');
    } else {
        console.error('Failed to order the transaction. Error code: ' + results[0].status);
    }

    if(results && results[1] && results[1].event_status === 'VALID') {
        console.log('Successfully committed the change to the ledger by the peer');
    } else {
        console.log('Transaction failed to be committed to the ledger due to ::'+results[1].event_status);
    }
}).catch((err) => {
    console.error('Failed to invoke successfully :: ' + err);
});
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息