使用fabric-sdk-node的示例2

0 導(dǎo)言

fabric-sdk-node也是分兩種寫法峡蟋,一種是底層寫法坟桅,一種是比較高層的寫法。下面先演示比較高層的寫法蕊蝗。借助于fabric-network這個模塊可以寫更加簡捷的代碼仅乓。

本文檔演示了一個汽車資產(chǎn)管理的例子,官方提供相應(yīng)的鏈碼和客戶端示例代碼蓬戚。

1 創(chuàng)建項目目錄

注:如果已經(jīng)有該目錄則不需 創(chuàng)建了

$ cd $GOPATH/src/github.com
$ mkdir -p fabric-study/sdk-node-study1

注:如果上述mkdir 不能執(zhí)行夸楣,可能是沒有權(quán)限,加上sudo就可以了

$ sudo mkdir -p fabric-study/sdk-node-study1

2 搭建運行網(wǎng)絡(luò)

我們不另行去搭建節(jié)點網(wǎng)絡(luò)了,直接拷貝官網(wǎng)提供的basic-network過來用豫喧,執(zhí)行cp命令進行拷貝.

$ cd fabric-study/sdk-node-study1/
$ cp -r ../../hyperledger/fabric-samples/basic-network ./

basic-network會創(chuàng)建如下節(jié)點

序號 組織 IP 域名 端口映射 節(jié)點
docker 容器1 172.18.0.6 orderer.example.com 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp orderer節(jié)點
docker 容器2 組織1 - org1 172.18.0.7 peer0.org1.example.com 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1節(jié)點
docker 容器3 172.18.0.6 cli客戶端
docker 容器4 172.18.0.6 couchDB服務(wù)器
docker 容器5 172.18.0.6 ca服務(wù)器

3 創(chuàng)建鏈碼放置目錄

在這里我們不打算自己編寫鏈碼了石洗,直接使用官方提供的fabcar示例的鏈碼,如若對該鏈碼不了解紧显,可通過閱讀該鏈碼的源碼來熟悉它

$ mkdir chaincode
$ cp -r ../../hyperledger/fabric-samples/chaincode/fabcar ./chaincode

4 創(chuàng)建app1目錄

$ mkdir app2

5 用vscode打開sdk-node-study2

Snipaste_2019-03-22_16-58-32.png

6 在app1下創(chuàng)建package.json

內(nèi)容如下

{
    "name": "fabcar",
    "version": "1.0.0",
    "description": "FabCar application implemented in JavaScript",
    "engines": {
        "node": ">=8",
        "npm": ">=5"
    },
    "scripts": {
        "lint": "eslint .",
        "pretest": "npm run lint",
        "test": "nyc mocha --recursive"
    },
    "engineStrict": true,
    "author": "Hyperledger",
    "license": "Apache-2.0",
    "dependencies": {
        "fabric-ca-client": "~1.4.0",
        "fabric-network": "~1.4.0"
    },
    "devDependencies": {
        "chai": "^4.2.0",
        "eslint": "^5.9.0",
        "mocha": "^5.2.0",
        "nyc": "^13.1.0",
        "sinon": "^7.1.1",
        "sinon-chai": "^3.3.0"
    },
    "nyc": {
        "exclude": [
            "coverage/**",
            "test/**"
        ],
        "reporter": [
            "text-summary",
            "html"
        ],
        "all": true,
        "check-coverage": true,
        "statements": 100,
        "branches": 100,
        "functions": 100,
        "lines": 100
    }
}

7 創(chuàng)建enrollAdmin.js

該js腳本用來向ca-服務(wù)器注冊管理員

'use strict';

//向ca服務(wù)器注冊管理員
const FabricCAServices = require('fabric-ca-client');
const { FileSystemWallet, X509WalletMixin } = require('fabric-network');
const fs = require('fs');
const path = require('path');

//connection.json這個文件可以在basic-network文件夾找到讲衫,配置了一些連接的信息
const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);

async function main() {
    try {

        // 創(chuàng)建一個CA客戶端
        const caURL = ccp.certificateAuthorities['ca.example.com'].url;
        const ca = new FabricCAServices(caURL);

        // 創(chuàng)建一個wallet文件夾,用于管理身份.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // 檢查是否已經(jīng)注冊管理員.
        const adminExists = await wallet.exists('admin');
        if (adminExists) {
            console.log('An identity for the admin user "admin" already exists in the wallet');
            return;
        }

        // 向ca服務(wù)器注冊管理員,并將從服務(wù)器獲得的身份證書導(dǎo)入到wallet.
        const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' });
        const identity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes());
        wallet.import('admin', identity);
        console.log('Successfully enrolled admin user "admin" and imported it into the wallet');

    } catch (error) {
        console.error(`Failed to enroll admin user "admin": ${error}`);
        process.exit(1);
    }
}

main();

8 創(chuàng)建registerUser.js

該js腳本用來向fabric-ca服務(wù)器注冊普通用戶


'use strict';

//注冊普通用戶
const { FileSystemWallet, Gateway, X509WalletMixin } = require('fabric-network');
const fs = require('fs');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);

async function main() {
    try {

        // 創(chuàng)建wallet文件夾用于管理身份信息.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // 檢查是否已經(jīng)注冊過user1用戶
        const userExists = await wallet.exists('user1');
        if (userExists) {
            console.log('An identity for the user "user1" already exists in the wallet');
            return;
        }

        // 檢查是否已經(jīng)注冊了admin用戶
        const adminExists = await wallet.exists('admin');
        if (!adminExists) {
            console.log('An identity for the admin user "admin" does not exist in the wallet');
            console.log('Run the enrollAdmin.js application before retrying');
            return;
        }

        // 創(chuàng)建一個用于連接peer節(jié)點的網(wǎng)關(guān)
        const gateway = new Gateway();
        await gateway.connect(ccp, { wallet, identity: 'admin', discovery: { enabled: false } });

        // 創(chuàng)建一個用于與ca 服務(wù)器交互的ca 客戶端對象
        const ca = gateway.getClient().getCertificateAuthority();
        const adminIdentity = gateway.getCurrentIdentity();

        // 注冊用戶到ca服務(wù)器鸟妙,并將從ca服務(wù)器獲得的身份信息導(dǎo)入到wallet文件夾.
        const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'user1', role: 'client' }, adminIdentity);
        const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: secret });
        const userIdentity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes());
        wallet.import('user1', userIdentity);
        console.log('Successfully registered and enrolled admin user "user1" and imported it into the wallet');

    } catch (error) {
        console.error(`Failed to register user "user1": ${error}`);
        process.exit(1);
    }
}

main();

9 創(chuàng)建query.js

該js腳本用來查詢汽車信息



'use strict';

//通過鏈碼查詢
const { FileSystemWallet, Gateway } = require('fabric-network');
const fs = require('fs');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);

async function main() {
    try {

        // 創(chuàng)建一個用于管理身份的文件夾wallet
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

        // 檢查是否已經(jīng)注冊好普通用戶user1
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

        // 創(chuàng)建一個用于與peer節(jié)點通信的網(wǎng)關(guān)對象
        const gateway = new Gateway();
        await gateway.connect(ccp, { wallet, identity: 'user1', discovery: { enabled: false } });

        // 獲得我們的鏈碼部署到的channel對象
        const network = await gateway.getNetwork('mychannel');

        // 通過鏈碼名稱獲得智能合約對象
        const contract = network.getContract('fabcar');

        // 執(zhí)行指定的交易.
        // queryCar 交易 - 需要1個參數(shù), 例如: ('queryCar', 'CAR4')
        // queryAllCars 交易 - 不需要任何參數(shù), 例如: ('queryAllCars')
        //注意焦人,如果不修改狀態(tài)使用evaluateTransaction
        const result = await contract.evaluateTransaction('queryAllCars');
        console.log(`Transaction has been evaluated, result is: ${result.toString()}`);

    } catch (error) {
        console.error(`Failed to evaluate transaction: ${error}`);
        process.exit(1);
    }
}

main();


10 創(chuàng)建invoke.js

該js腳本做invoke調(diào)用

'use strict';

//調(diào)用鏈碼對應(yīng)的函數(shù)
const { FileSystemWallet, Gateway } = require('fabric-network');
const fs = require('fs');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);

async function main() {
    try {

        // 創(chuàng)建一個用于管理身份的文件夾wallet
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

       // 檢查是否已經(jīng)注冊好普通用戶user1
        const userExists = await wallet.exists('user1');
        if (!userExists) {
            console.log('An identity for the user "user1" does not exist in the wallet');
            console.log('Run the registerUser.js application before retrying');
            return;
        }

      // 創(chuàng)建一個用于與peer節(jié)點通信的網(wǎng)關(guān)對象
        const gateway = new Gateway();
        await gateway.connect(ccp, { wallet, identity: 'user1', discovery: { enabled: false } });

         // 獲得我們的鏈碼部署到的channel對象
        const network = await gateway.getNetwork('mychannel');

        // 創(chuàng)建一個用于與peer節(jié)點通信的網(wǎng)關(guān)對象
        const contract = network.getContract('fabcar');

        // 提交特定的交易 .
        // createCar 交易 - 需要5個參數(shù), 例如: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom')
        // changeCarOwner 交易 - 需要2個參數(shù) , 例如: ('changeCarOwner', 'CAR10', 'Dave')
        //注意挥吵,涉及到修改狀態(tài)使用submitTransaction
        await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
        console.log('Transaction has been submitted');

        // 斷開網(wǎng)關(guān)連接
        await gateway.disconnect();

    } catch (error) {
        console.error(`Failed to submit transaction: ${error}`);
        process.exit(1);
    }
}

main();

11 啟動網(wǎng)絡(luò)

生成證書(注重父,只需要執(zhí)行一次就ok了,以后都不用執(zhí)行這一步了

$ ./generate.sh

先停止到舊的網(wǎng)絡(luò)


$ docker stop -f $(docker ps -aq)
$ docker rm -f $(docker ps -aq)
$ docker rmi -f $(docker images | grep fabcar | awk '{print $3}')

然后啟動網(wǎng)絡(luò),該腳本只啟動了ca、orderer忽匈、peer房午、couchDb這幾個容器,沒有啟動cli容器丹允。并且創(chuàng)建名字為mychannel的通道郭厌,隨后還讓peer0.Org1加入了該通道

$ rm -rf ./hfc-key-store
$ cd basic-network
$ ./start.sh

12 啟動cli容器

$ docker-compose -f ./docker-compose.yml up -d cli

13 安裝和實例化鏈碼

打開一個新的終端,不用進入cli容器雕蔽,可以在外邊執(zhí)行折柠,用-exec表示在外邊執(zhí)行

安裝鏈碼

$ 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 fabcar -v 1.0 -p github.com/fabcar/go -l golang

實例化鏈碼

$ 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 fabcar -l golang -v 1.0 -c '{"Args":[]}' -P "OR ('Org1MSP.member','Org2MSP.member')"

調(diào)用initLedger函數(shù)初始化賬本

$ 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 fabcar -c '{"function":"initLedger","Args":[]}'

14 進入app1目錄安裝依賴

到了這一步,證書已經(jīng)生成批狐,節(jié)點網(wǎng)絡(luò)已經(jīng)啟動扇售,鏈碼也安裝和實例化好了,接下來就來測試一下sdk的調(diào)用是否成功了嚣艇。

進入app1目錄,并執(zhí)行npm install安裝依賴模塊

$ cd app1
$ npm install

15 注冊管理員

$ node enrollAdmin.js

輸出如下信息

Store path:/home/bob/go/src/github.com/fabric-study/sdk-node-study1/app1/hfc-key-store
Successfully enrolled admin user "admin"
Assigned the admin user to the fabric client ::{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"607d571669d90da409ebdaec5855e3a395bbf7af5aab200cd5a902a4054c9cb0","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICAjCCAaigAwIBAgIUGuRlUgmDVeRPKDsf67/6fom2bvMwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTkwMzIyMDY0NjAwWhcNMjAwMzIxMDY1\nMTAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAE4UAK/Z9BFZE7Fji6S4KTTY/2DGK6Vz0FtuhOQStE\n0rQ5PfwcbUEYFP/Z1m3Lhtvkly09eFWM+vibivprOQAbzaNsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFja6ma5kc1yk5ax5h0Fc0zl\niI3XMCsGA1UdIwQkMCKAIEI5qg3NdtruuLoM2nAYUdFFBNMarRst3dusalc2Xkl8\nMAoGCCqGSM49BAMCA0gAMEUCIQDr6unOLXftqasK83fkthyfykHNI4Mt7y7DA+lX\ns2PyHQIgS54OqyzJ0kulh/xyHd9IkDRAM2iJPVsxCzMUP+t/C1s=\n-----END CERTIFICATE-----\n"}}}

16 注冊普通用戶

$ node register.js

輸出如下信息

 Store path:/home/bob/go/src/github.com/fabric-study/sdk-node-study1/app1/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 - secret:OhvdSyIJrqqM
Successfully enrolled member user "user1" 
User1 was successfully registered and enrolled and is ready to interact with the fabric network

17 執(zhí)行查詢

$ node query.js

可以看到承冰,所有的汽車被查出來

Query has completed, checking results
Response is  [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]

18 執(zhí)行調(diào)用

$ node invoke.js
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市食零,隨后出現(xiàn)的幾起案子困乒,更是在濱河造成了極大的恐慌,老刑警劉巖贰谣,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娜搂,死亡現(xiàn)場離奇詭異,居然都是意外死亡吱抚,警方通過查閱死者的電腦和手機涌攻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來频伤,“玉大人恳谎,你說我怎么就攤上這事。” “怎么了因痛?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵婚苹,是天一觀的道長。 經(jīng)常有香客問我鸵膏,道長膊升,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任谭企,我火速辦了婚禮廓译,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘债查。我一直安慰自己非区,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布盹廷。 她就那樣靜靜地躺著征绸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俄占。 梳的紋絲不亂的頭發(fā)上管怠,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音缸榄,去河邊找鬼渤弛。 笑死,一個胖子當(dāng)著我的面吹牛甚带,可吹牛的內(nèi)容都是我干的她肯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼欲低,長吁一口氣:“原來是場噩夢啊……” “哼辕宏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起砾莱,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤瑞筐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腊瑟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體聚假,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年闰非,在試婚紗的時候發(fā)現(xiàn)自己被綠了膘格。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡财松,死狀恐怖瘪贱,靈堂內(nèi)的尸體忽然破棺而出纱控,到底是詐尸還是另有隱情,我是刑警寧澤菜秦,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布甜害,位于F島的核電站,受9級特大地震影響球昨,放射性物質(zhì)發(fā)生泄漏尔店。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一主慰、第九天 我趴在偏房一處隱蔽的房頂上張望嚣州。 院中可真熱鬧,春花似錦共螺、人聲如沸该肴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沙庐。三九已至鲤妥,卻和暖如春佳吞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棉安。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工底扳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贡耽。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓衷模,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒲赂。 傳聞我的和親對象是個殘疾皇子阱冶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容