Hyperledger Fabric 客户端开发一

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

 

同传统的互联网开发同样, 能够理解为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等。java

 

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

 

SDK版本

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

官方支持的版本:github

非官方的版本:json

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

 

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());
相关文章
相关标签/搜索