博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hyperledger Fabric 客户端开发一
阅读量:7047 次
发布时间:2019-06-28

本文共 7041 字,大约阅读时间需要 23 分钟。

hot3.png

前面介绍了hyperledger Fabric 安装, Chaincode的开发和运维, 现在来说说hyperledger fabric的客户端相关的开发。hyperledger 的客户端开发, 实际上指的是Chaincode的客户端开发。

 

同传统的互联网开发一样, 可以理解为hyperledger fabric是C/S架构, 当然这样的类比不是很严谨。那么, 以前的服务端API在hyperledger fabric中相当于Chaincode开发, 以前的容器或者其他类似与Tomcat, Nginx 的服务器相当于 hyperledger 中的 Blockchain 本身, 当然在Blockchain中, 数据存储也在Blockchain上, 所以Blochchain也是新的存储平台, 而传统的客户端开发, 其实就是通过SDK或者Restful APIs 和服务端进行交互, 在hyperledger中, 同样可以使用SDK 或者 Restful APIs 来和服务端进行交互, 只是, 这个交互不仅仅适合自己定义的Chaincode中的业务逻辑来进行交互, 也和Chaincode本身进行交互, 在hyperledger中, 例如客户端application 通过SDK连接访问 peer, channel, orderer , Block, Transaction等。

 

总的来说, Hyperledger Fabric 客户端开发主要包括通过Chaincode定义业务逻辑, 来改变Blockchain的状态, 通过SDK来和Blockchain进行通讯。

 

SDK版本

Hyperledger Fabric 提供了多种语言的SDK版本, 目前主要包括:

官方支持的版本:

非官方的版本:

其中, 以 Hyperledger Fabric Node SDK的文档最为详细, 这里以 为例来说明Hyperledger Fabric客户端开发。

 

API作用

在 Hyperledger Fabric 的SDK中, 提供的API的作用主要有:

  • 创建通道 (create channel)
  • 请求节点加入通道 (join channel)
  • 在节点中安装链码 (install chaincode)
  • 通过调用链码来调用事物 (invoke chaincode)
  • 查询事物或者区块的账本 (query chaincode)

在  中提供了一个应用程序(SDK), peer 和 orderer 共同处理事物并产生区块的流程。Fabric的安全是通过数字签名来实现的, 在Fabric中所有的请求都必须具有有效注册证书的用户签名。对于在Fabric中被认为有效的证书, 必须具有受信任的证书颁发机构签名。Fabirc支持CA的所有标准, Fabric 同时提供了一个可选的CA实现。

 

Node SDK 模块

Node SDK由3个顶级模块组成:

  • api : 可插拔式的API, 可自由定制, SDK提供默认实现
  • fabric-client :提供API用来同Hyperledger Fabirc的区块链网络进行交互, 具体就是同peer, orderer 和事件流交互。
  • fabric-ca-client:该API用来同fabirc提供的可选的CA实现Hyperledger Fabric CA交互, 该CA提供成员管理服务。

 

Node SDK 功能

fabric-client:

  • 创建一个新的通道(create channel)
  • 将通道信息发送给节点用于节点加入 (join channel)
  • 在节点上安装链码 (install chaincode)
  • 在通道中进行链码实例化 (instantiate chaincode)
  • 提交交易, 包括 :提案和交易 (invoke)
  • 查询链码的最新状态 (query)
  • 多种查询功能
  1. 查询通道长度
  2. 通过区块高度查询, 通过区块hash查询区块
  3. 查询一个节点所在的所有通道
  4. 查询节点中安装的所有链码
  5. 查询通道中的所有实例化链码
  6. 通过tansaction ID查询交易
  7. 查询通道的配置数据
  • 监控事件

连接一个节点的事件流

监控一个区块事件

监控交易事件和结果

坚挺链码自定义事件

  • 序列化用户对象和签名功能
  • 多层覆盖式的风层配置设置
  • 日志定义
  • 可插拔式的CryptoSuite
  • 可插拔式的状态存储
  • 自定义的密钥存储
  • 支持TLS和非TLS连接到peer 和orderer

fabric-ca-client:

  • 注册新用户 (register)
  • 登记用户同时获取有Fabric CA签署的注册证书(enroll)
  • 通过注册ID废除已有用户或废除特定证书 (revoke)
  • 自定义的持久化存储

 

实例

下面通过一个实例来说明Hyperledger Fabric客户端开发。一下是一段Chaincode, 定义了业务逻辑。

【有点类似传统的管理系统开发, chaincode实现CURD的功能, 通过SDK与fabric 交互, 来达到Blockchain状态的改变, 只是, chaincode支持byte和json数据,并且是以KV的形式存储】

/*# Copyright IBM Corp. All Rights Reserved.## SPDX-License-Identifier: Apache-2.0*/'use strict';const shim = require('fabric-shim');const util = require('util');let Chaincode = class {  // The Init method is called when the Smart Contract 'fabcar' is instantiated by the blockchain network  // Best practice is to have any Ledger initialization in separate function -- see initLedger()  async Init(stub) {    console.info('=========== Instantiated fabcar chaincode ===========');    return shim.success();  }  // The Invoke method is called as a result of an application request to run the Smart Contract  // 'fabcar'. The calling application program has also specified the particular smart contract  // function to be called, with arguments  async Invoke(stub) {    let ret = stub.getFunctionAndParameters();    console.info(ret);    let method = this[ret.fcn];    if (!method) {      console.error('no function of name:' + ret.fcn + ' found');      throw new Error('Received unknown function ' + ret.fcn + ' invocation');    }    try {      let payload = await method(stub, ret.params);      return shim.success(payload);    } catch (err) {      console.log(err);      return shim.error(err);    }  }  async queryCar(stub, args) {    if (args.length != 1) {      throw new Error('Incorrect number of arguments. Expecting CarNumber ex: CAR01');    }    let carNumber = args[0];    let carAsBytes = await stub.getState(carNumber); //get the car from chaincode state    if (!carAsBytes || carAsBytes.toString().length <= 0) {      throw new Error(carNumber + ' does not exist: ');    }    console.log(carAsBytes.toString());    return carAsBytes;  }  async initLedger(stub, args) {    console.info('============= START : Initialize Ledger ===========');    let cars = [];    cars.push({      make: 'Toyota',      model: 'Prius',      color: 'blue',      owner: 'Tomoko'    });    cars.push({      make: 'Ford',      model: 'Mustang',      color: 'red',      owner: 'Brad'    });    cars.push({      make: 'Hyundai',      model: 'Tucson',      color: 'green',      owner: 'Jin Soo'    });    cars.push({      make: 'Volkswagen',      model: 'Passat',      color: 'yellow',      owner: 'Max'    });    cars.push({      make: 'Tesla',      model: 'S',      color: 'black',      owner: 'Adriana'    });    cars.push({      make: 'Peugeot',      model: '205',      color: 'purple',      owner: 'Michel'    });    cars.push({      make: 'Chery',      model: 'S22L',      color: 'white',      owner: 'Aarav'    });    cars.push({      make: 'Fiat',      model: 'Punto',      color: 'violet',      owner: 'Pari'    });    cars.push({      make: 'Tata',      model: 'Nano',      color: 'indigo',      owner: 'Valeria'    });    cars.push({      make: 'Holden',      model: 'Barina',      color: 'brown',      owner: 'Shotaro'    });    for (let i = 0; i < cars.length; i++) {      cars[i].docType = 'car';      await stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i])));      console.info('Added <--> ', cars[i]);    }    console.info('============= END : Initialize Ledger ===========');  }  async createCar(stub, args) {    console.info('============= START : Create Car ===========');    if (args.length != 5) {      throw new Error('Incorrect number of arguments. Expecting 5');    }    var car = {      docType: 'car',      make: args[1],      model: args[2],      color: args[3],      owner: args[4]    };    await stub.putState(args[0], Buffer.from(JSON.stringify(car)));    console.info('============= END : Create Car ===========');  }  async queryAllCars(stub, args) {    let startKey = 'CAR0';    let endKey = 'CAR999';    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));      }    }  }  async changeCarOwner(stub, args) {    console.info('============= START : changeCarOwner ===========');    if (args.length != 2) {      throw new Error('Incorrect number of arguments. Expecting 2');    }    let carAsBytes = await stub.getState(args[0]);    let car = JSON.parse(carAsBytes);    car.owner = args[1];    await stub.putState(args[0], Buffer.from(JSON.stringify(car)));    console.info('============= END : changeCarOwner ===========');  }};shim.start(new Chaincode());

转载于:https://my.oschina.net/kingwjb/blog/1861511

你可能感兴趣的文章
[Ubuntu] ubuntu的tty下挂载移动硬盘拷贝数据
查看>>
PyGObject的使用手册
查看>>
找到多个与名为“Home”的控制器匹配的类型
查看>>
【编程基础】C语言常见宏定义
查看>>
CAS与OAuth2的区别
查看>>
python3中的List
查看>>
Sqlserver创建连接MySql的链接服务器
查看>>
ORACLE中常见SET指令
查看>>
Android startActivities()的使用
查看>>
MySQL环境部署
查看>>
Could not find a storyboard named 'Main' in bundle NSBundle
查看>>
bzoj 3217: ALOEXT
查看>>
轻松掌握shell编程中数组的常见用法及示例
查看>>
要想真正牛逼,唯有用实力和“拳头”说话。
查看>>
PowerBI分析个人Exchange邮箱数据
查看>>
犯了个低级错误
查看>>
Win7部署基础知识(7):使用Imagex捕获和安装映像
查看>>
Outlook Anywhere 客户端配置详解
查看>>
优化系列 | MySQL 5.6.10 vs Percona 5.6.6之TPCCC性能测试
查看>>
Windows 9操作系统已经开始测试!
查看>>