fabric nodejs鏈碼示例2

0 導(dǎo)言

智能合約是區(qū)塊鏈中一個(gè)非常重要的概念和組成部分遭商。在Fabric中內(nèi)成為Chaincode,中文翻譯為鏈碼厅各。涉及到鏈碼地方都是 Chaincode.

本示例是一個(gè)資產(chǎn)交易的示例

主要實(shí)現(xiàn)如下的功能:

  • 初始化 A抵碟、B 兩個(gè)賬戶,并為兩個(gè)賬戶賦初始資產(chǎn)值祥山;
  • 在 A、B 兩個(gè)賬戶之間進(jìn)行資產(chǎn)交易掉伏;
  • 分別查詢 A缝呕、B 兩個(gè)賬戶上的余額,確認(rèn)交易成功斧散;
  • 刪除賬戶供常。
  • 新增賬戶

主要函數(shù)

  • Init:初始化 A、B 兩個(gè)賬戶鸡捐;
  • Invoke:調(diào)用其它函數(shù)的入口栈暇;
  • transfer:實(shí)現(xiàn) A、B 賬戶間的轉(zhuǎn)賬箍镜;
  • query:查詢 A源祈、B 賬戶上的余額;
  • delete:刪除賬戶色迂。
  • create: 新增賬戶

注意:Fabric官方提供了兩種開(kāi)發(fā)node.js鏈碼的途徑:fabric-shim和fabric-contract-api香缺。下面演示fabric-contract-api的方式

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

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

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

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

$ sudo mkdir chaincode-study1

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

我們不另行去搭建節(jié)點(diǎn)網(wǎng)絡(luò)了图张,直接拷貝官網(wǎng)提供的chaincode-docker-devmode過(guò)來(lái)用,執(zhí)行cp命令進(jìn)行拷貝

$ cd fabric-study/chaincode-study1/
$ cp -r ../../hyperledger/fabric-samples/chaincode-docker-devmode ./

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

$ mkdir -p chaincode/javascript

4 用開(kāi)發(fā)工具vs code打開(kāi)chaincode-study1目錄

Snipaste_2019-03-22_10-00-36.png

5 創(chuàng)建相應(yīng)的目錄和文件

創(chuàng)建index.js和package.json文件,再創(chuàng)建lib目錄祸轮,在lib目錄下再創(chuàng)建example.js


Snipaste_2019-03-22_11-45-17.png

package.json文件內(nèi)容如下

{
    "name": "example",
    "version": "1.0.0",
    "description": "Example contract implemented in JavaScript",
    "main": "index.js",
    "engines": {
        "node": ">=8",
        "npm": ">=5"
    },
    "scripts": {
        "lint": "eslint .",
        "pretest": "npm run lint",
        "test": "nyc mocha --recursive",
        "start": "fabric-chaincode-node start --peer.address peer:7052 --chaincode-id-name mycc:0"
    },
    "engineStrict": true,
    "author": "Hyperledger",
    "license": "Apache-2.0",
    "dependencies": {
        "fabric-contract-api": "~1.4.0",
        "fabric-shim": "~1.4.0"
    },
    "devDependencies": {
        "chai": "^4.1.2",
        "eslint": "^4.19.1",
        "mocha": "^5.2.0",
        "nyc": "^12.0.2",
        "sinon": "^6.0.0",
        "sinon-chai": "^3.2.0"
    },
    "nyc": {
        "exclude": [
            "coverage/**",
            "test/**"
        ],
        "reporter": [
            "text-summary",
            "html"
        ],
        "all": true,
        "check-coverage": true,
        "statements": 100,
        "branches": 100,
        "functions": 100,
        "lines": 100
    }
}

可以看到要依賴"fabric-contract-api": "~1.4.0"和"fabric-shim": "1.4.0"這個(gè)包

6 寫(xiě)一個(gè)Example類并提供構(gòu)造函數(shù)

打開(kāi)example.js開(kāi)始編寫(xiě)代碼

const { Contract } = require('fabric-contract-api');

class Example extends Contract {

    constructor(){
        super('ExampleContract');
    }/這里寫(xiě)各種函數(shù)
}

7 創(chuàng)建instantiate函數(shù)

    //對(duì)應(yīng)peer node instantiate
    async instantiate(ctx,A,Aval,B,Bval){
        if(!A || !Aval || !B || !Bval){
            throw new Error("必須傳入4個(gè)參數(shù)");
        }
        if (typeof parseInt(Aval) !== 'number' || typeof parseInt(Bval) !== 'number') {
            throw new Error('資產(chǎn)值必須是一個(gè)整型數(shù)');
        }

        await ctx.stub.putState(A, Buffer.from(Aval));
        await ctx.stub.putState(B, Buffer.from(Bval));
    }

8 創(chuàng)建transfer函數(shù)

 async transfer(ctx,A,B,amount){
        if(!A || !B || !amount){
            throw new Error("必須傳入3個(gè)參數(shù)");
        }

        let Avalbytes = await ctx.stub.getState(A);
        if (!Avalbytes || Avalbytes.length === 0) {
            throw new Error('從資產(chǎn)持有者'+A+'獲取狀態(tài)失敗');
        }
        let Aval = parseInt(Avalbytes.toString());

        let Bvalbytes = await ctx.stub.getState(B);
        if (!Bvalbytes || Bvalbytes.length === 0) {
            throw new Error('從資產(chǎn)持有者'+B+'獲取狀態(tài)失敗');
        }
        let Bval = parseInt(Bvalbytes.toString());

        let X = parseInt(amount);
        if (typeof X !== 'number') {
            throw new Error('轉(zhuǎn)賬數(shù)值必須是一個(gè)整型數(shù)');
        }
        if (Aval < X) {
            throw new Error('余額不足');
        }
      
        Aval = Aval - X;
        Bval = Bval + X;
        console.info(util.format('Aval = %d, Bval = %d\n', Aval, Bval));
        await ctx.stub.putState(A, Buffer.from(Aval.toString()));
        await ctx.stub.putState(B, Buffer.from(Bval.toString()));
    }

9 創(chuàng)建delete函數(shù)

     // 刪除賬戶實(shí)體
     async delete(ctx, A) {
        if (!A) {
            throw new Error('不正確的參數(shù)個(gè)數(shù)姑隅,期望1個(gè)參數(shù)');
        }

        // Delete the key from the state in ledger
        await ctx.stub.deleteState(A);
    }

10 創(chuàng)建query函數(shù)

    // 查詢賬戶的資產(chǎn)
    async query(ctx, A) {
        if (!A) {
            throw new Error('不正確的參數(shù)個(gè)數(shù),期望1個(gè)參數(shù)');
        }

        let jsonResp = {};
        // Get the state from the ledger
        let Avalbytes = await ctx.stub.getState(A);
        if (!Avalbytes  || Avalbytes.length === 0) {
            jsonResp.error = '從資產(chǎn)持有者'+A+'獲取狀態(tài)失敗';
            throw new Error(JSON.stringify(jsonResp));
        }

        jsonResp.name = A;
        jsonResp.amount = Avalbytes.toString();
        console.info('Query Response:');
        console.info(jsonResp);
        return parseInt(Avalbytes.toString());
    }

11 創(chuàng)建create函數(shù)


    // 創(chuàng)建賬戶實(shí)體
    async create(ctx, A,Aval) {
        if (!A || !Aval) {
            throw new Error('不正確的參數(shù)個(gè)數(shù)倔撞,期望2個(gè)參數(shù)')
        }

        let jsonResp = {}; 
        if (typeof parseInt(Aval) !== 'number') {
            return shim.error('期望一個(gè)整型值');
        }

        try {
            await ctx.stub.putState(A, Buffer.from(Aval));
        } catch (err) {
            return shim.error(err);
        }
    }

12 文件的最后別忘了導(dǎo)出

在example.js中導(dǎo)出

module.exports = Example;

在index.js中同樣要導(dǎo)出

'use strict';

const Example = require('./lib/example');

module.exports.Example = Example;
module.exports.contracts = [ Example ];

13 啟動(dòng)節(jié)點(diǎn)網(wǎng)絡(luò)

打開(kāi)第1個(gè)終端

先停掉和刪除已有的容器

$ docker stop $(docker ps -aq)
$ docker rm $(docker ps -aq)

執(zhí)行下面的命令啟動(dòng)節(jié)點(diǎn)網(wǎng)絡(luò)

$ cd chaincode-docker-devmode
$ docker-compose -f docker-compose-simple.yaml up

14 安裝依賴包并執(zhí)行

打開(kāi)第2個(gè)終端

進(jìn)入chaincode這個(gè)容器

$ docker exec -it chaincode bash

cd到chaincode/go目錄下

$ cd javascript

安裝依賴包

$ npm install

如果卡住了用淘寶鏡像

$ npm install --registry=http://registry.npm.taobao.org

運(yùn)行

$ npm run start

15 安裝和實(shí)例化鏈碼

打開(kāi)第3個(gè)終端

進(jìn)入cli這個(gè)容器

$ docker exec -it cli bash

安裝鏈碼

$ peer chaincode install -p chaincode/javascript -n mycc -v 0 -l node

實(shí)例化鏈碼

$ peer chaincode instantiate -n mycc -v 0 -c '{"Args":["ExampleContract:instantiate","tom","100","bob","200"]}' -C myc

16 執(zhí)行新建,查詢慕趴,轉(zhuǎn)賬痪蝇,刪除等函數(shù)

還是在 第3個(gè)終端

新建賬戶實(shí)體

$ peer chaincode invoke -n mycc -c '{"Args":["ExampleContract:create","lily","150"]}' -C myc

查詢

$ peer chaincode query -n mycc -c '{"Args":["ExampleContract:query","lily"]}' -C myc
$ peer chaincode query -n mycc -c '{"Args":["ExampleContract:query","tom"]}' -C myc
$ peer chaincode query -n mycc -c '{"Args":["ExampleContract:query","bob"]}' -C myc

轉(zhuǎn)賬

bob給lily轉(zhuǎn)賬30

$ peer chaincode invoke -n mycc -c '{"Args":["ExampleContract:transfer","bob","lily","30"]}' -C myc

轉(zhuǎn)賬后再查詢

$ peer chaincode query -n mycc -c '{"Args":["ExampleContract:query","lily"]}' -C myc
$ peer chaincode query -n mycc -c '{"Args":["ExampleContract:query","bob"]}' -C myc

刪除tom賬戶實(shí)體

$ peer chaincode invoke -n mycc -c '{"Args":["ExampleContract:delete","tom"]}' -C myc

17 Chaincode 說(shuō)明

Fabric中的Chaincode包含了一個(gè)Chaincode代碼和Chaincode管理命令這兩部分。
Chaincode 代碼是業(yè)務(wù)的承載體冕房,負(fù)責(zé)具體的業(yè)務(wù)邏輯
Chaincode 管理命令負(fù)責(zé) Chaincode的部署躏啰,安裝,維護(hù)等工作

17.1 Chaincode代碼

Fabric的Chaincode是一段運(yùn)行在容器中的程序耙册。Chaincode是客戶端程序和Fabric之間的橋梁给僵。通過(guò)Chaincode客戶端程序可以發(fā)起交易,查詢交易详拙。Chaincode是運(yùn)行在Dokcer容器中帝际,因此相對(duì)來(lái)說(shuō)安全。

目前支持 java,node,go饶辙。

17.2 Chaincode的管理命令

Chaincode管理命令主要用來(lái)對(duì)Chaincode進(jìn)行安裝蹲诀,實(shí)例化,調(diào)用弃揽,打包脯爪,簽名操作。Chaincode命令包含在Peer模塊中矿微,是peer模塊中一個(gè)子命令. 可通過(guò)執(zhí)行peer chaincode --help查看如何使用痕慢。

peer chaincode --help

Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.

Usage:
  peer chaincode [command]

Available Commands:
  install     Package the specified chaincode into a deployment spec and save it on the peer's path.
  instantiate Deploy the specified chaincode to the network.
  invoke      Invoke the specified chaincode.
  list        Get the instantiated chaincodes on a channel or installed chaincodes on a peer.
  package     Package the specified chaincode into a deployment spec.
  query       Query using the specified chaincode.
  signpackage Sign the specified chaincode package
  upgrade     Upgrade chaincode.

17.3 chaincode的生命周期

fabric 提供了4種命令去管理chaincode生命周期:package、install涌矢、instantiate掖举、upgrade。將來(lái)發(fā)布的版本的會(huì)增加stop以及start娜庇。

transaction用于停止與開(kāi)啟chaincode拇泛,而不用去卸載chaincode。chaincode在成功install以及instantiate之后思灌,chaincode則是運(yùn)行狀態(tài)俺叭,能夠通過(guò)invoke transaction來(lái)處理交易。后續(xù)也能夠?qū)haincode進(jìn)行升級(jí)泰偿。


Snipaste_2019-03-22_06-53-26.png

17.4 fabric的nodejs鏈碼結(jié)構(gòu)

fabric-shim是一種相對(duì)底層的fabric grpc協(xié)議封裝熄守,它直接把鏈碼接口暴露給開(kāi)發(fā)者,雖然簡(jiǎn)單直白,但如果要實(shí)現(xiàn)相對(duì)復(fù)雜一點(diǎn)的鏈碼裕照,開(kāi)發(fā)者需要自己在Invoke實(shí)現(xiàn)中進(jìn)行方法路由攒发。
fabric-contract-api則是更高層級(jí)的封裝,開(kāi)發(fā)者直接繼承開(kāi)發(fā)包提供的Contract類晋南,就不用費(fèi)心合約方法路由的問(wèn)題了

nodejs鏈碼需要引入fabric-shim和util模塊

const { Contract } = require('fabric-contract-api');
const util = require('util');
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惠猿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子负间,更是在濱河造成了極大的恐慌偶妖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件政溃,死亡現(xiàn)場(chǎng)離奇詭異趾访,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)董虱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)扼鞋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人愤诱,你說(shuō)我怎么就攤上這事云头。” “怎么了淫半?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵盘寡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撮慨,道長(zhǎng)竿痰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任砌溺,我火速辦了婚禮影涉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘规伐。我一直安慰自己蟹倾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布猖闪。 她就那樣靜靜地躺著鲜棠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪培慌。 梳的紋絲不亂的頭發(fā)上豁陆,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音吵护,去河邊找鬼盒音。 笑死表鳍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祥诽。 我是一名探鬼主播譬圣,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雄坪!你這毒婦竟也來(lái)了厘熟?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤维哈,失蹤者是張志新(化名)和其女友劉穎绳姨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體笨农,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年帖渠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谒亦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡空郊,死狀恐怖份招,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狞甚,我是刑警寧澤锁摔,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站哼审,受9級(jí)特大地震影響谐腰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涩盾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一十气、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧春霍,春花似錦砸西、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至莲趣,卻和暖如春鸳慈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喧伞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工蝶涩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留理朋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓绿聘,卻偏偏與公主長(zhǎng)得像嗽上,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熄攘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348