文档章节

Hyperledger Fabric 客户端开发三

十一月不远
 十一月不远
发布于 07/23 16:03
字数 1664
阅读 12
收藏 2

前面两篇文章介绍了Hyperledger Fabric SDK并使用一个实例介绍如何通过SDK和Hyperledger Fabric Blockchain交互, 现在详细分析相关的过程。

 

首先看 enroll (登录) 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);
});

 

和其他所有程序一样, 首先需要加载相应的lib, 这里加载 fabric-client 和 fabric-ca-client.

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

然后初始化Fabric_Client 和 Fabric_CA_Client对象

var fabric_client = new Fabric_Client();
var fabric_ca_client = null;  
//fabric_ca_client =  new Fabric_CA_Client("http://localhost:7054", tlsOption, "ca.example.com", crypto_suite) 

 

同时需要创建一个admin_user对象, 一个member_user对象,和一个目录对象用来存储用户持久化信息和证书信息。

var admin_user = null;
var member_user = null;
var store_path = path.join(__dirname, "hfc-key-store"); //指定当前目录下的hfc_key_store文件夹

 

生成key value 存储 (create the key value store)

接下来创建key-value store, 这里调用Fabric_Client的static方法, 并且根据fabric-client/config/default.json配置文件中的key-value-store决定实现, 也就是前面说的Node SDK的默认实现。

我们看源码:

Fabric_Client.newDefaultKeyValueStore({path: store_path})
             .then((state_store) => {
                  //
              })...

此方法调用的是BaseClient.js的接口, 该接口位于 fabric-client/lib/BaseClient.js。

  var sdkUtils = require('./utils.js');

  static newDefaultKeyValueStore(options) {
                return sdkUtils.newKeyValueStore(options);
        }

通过阅读源码发现, 实际上调用的是sdkUtils的方法, 该方法实际上调用fabric-client/lib/utils.js的 newKeyValueStore方法。

// Provide a Promise-based keyValueStore for couchdb, etc.
module.exports.newKeyValueStore = function(options) {
        // initialize the correct KeyValueStore
        var kvsEnv = this.getConfigSetting('key-value-store');
        var store = require(kvsEnv);
        return Promise.resolve(new store(options));
};

此处, 程序首先从获取key-value-store的配置, 然后动态加载该模块, 然后返回Promise。可以看出, SDK提供了不同的KeyvalueStore实现, 默认为FileKeyValueStore实现, fabric-client/config/default.json中指定。

我们看default.json文件

{
	...
	"key-value-store": "fabric-client/lib/impl/FileKeyValueStore.js",
	"certificate-authority-client": "fabric-ca-client",
	"nonce-size" : 24,
	...
}

在fabric-client/lib/impl下可以看到, SDK提供了2种不同的实现:

FileKeyValueStore , CouchDBKeyValueStore.

我们看FileKeyValueStore的实现。在FileKeyValueStore的构造函数中, 有这么一段:

// Create the keyValStore instance
                super();

                var self = this;
                this._dir = options.path;
                return new Promise(function (resolve, reject) {
                        fs.mkdirs(self._dir, function (err) {
                                if (err) {
                                        logger.error('constructor, error creating directory, code: %s' , err.code);
                                        return reject(err);
                                }
                                return resolve(self);
                        });
                });

这一步执行后, 生成上面指定的hfc-key-store目录。 在看 FileKeyValueStore, 实际上实现了 setKey(name, value) 和 getValue(name) 方法。

配置fabric_client, 生成CryptoSuite

上面步骤执行成功后, 拿到KeyValueStore对象,然后执行一下操作:

// 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);

1 生成CrytoKeyStore, 用来存储用户的密钥和用户的证书(状态)。

     var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});

这里调用的是BaseClient.js的方法:

  static newCryptoKeyStore(KVSImplClass, opts) {
                return sdkUtils.newCryptoKeyStore(KVSImplClass, opts);
        }

这里调用util.js中的方法

module.exports.newCryptoKeyStore = function(KVSImplClass, opts) {
        // this function supports skipping any of the arguments such that it can be called in any of the following fashions:
        // - newCryptoKeyStore(CouchDBKeyValueStore, {name: 'member_db', url: 'http://localhost:5984'})
        // - newCryptoKeyStore({path: '/tmp/app-state-store'})
        // - newCryptoKeyStore()
        return new CryptoKeyStore(KVSImplClass, opts);
};

2 生成CryptoSuite

var crypto_suite = Fabric_Client.newCryptoSuite();
crypto_suite.setCryptoKeyStore(crypto_store);

3 指定 fabric_client的StateStore 和 CryptoSuite

fabric_client.setStateStore(state_store);
fabric_client.setCryptoSuite(crypto_suite);

4 连接到Fabric CA

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);

5 检查admin是否enroll

fabric_client.getUserContext('admin', true);

这一步返回一个User对象。true用于检测是否持久化。

reroll & register

同样看代码:

  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');
        });
    }

这里检测User对象是否已经reroll, 否则先reroll, 然后register用户。

1 检测用户状态

if (user_from_store && user_from_store.isEnrolled()) {
        console.log('Successfully loaded admin from persistence');
        admin_user = user_from_store;
        return null;
    }

这里User的API文档见:Hyperledger Fabric SDK for node.js Class: User 。

如果检测到用户已经reroll, 则说明当前用户已经reroll, 并且证书有效, 可以使用此证书进行后续操作, 否则需要注册用户。

2 reroll

 return fabric_ca_client.enroll({
          enrollmentID: 'admin',
          enrollmentSecret: 'adminpw'
        }).then((enrollment) => {
//
        });

此处的admin用户为配置文件中的启动用户,或者说是引导用户身份。或者应该是注册的具有相关权限的用户。

这一步执行后, 在hfc-key-store目录下产生af37da4c94057cd98f288a671703d346c7a79a79acd28ea8de7bf03cf04baf36-priv 文件, 内容为:

ThinkPad-L430:/opt/fabric-samples/fabcar$ ls hfc-key-store/
af37da4c94057cd98f288a671703d346c7a79a79acd28ea8de7bf03cf04baf36-priv
ThinkPad-L430:/opt/fabric-samples/fabcar$ cat hfc-key-store/af37da4c94057cd98f288a671703d346c7a79a79acd28ea8de7bf03cf04baf36-priv 
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBqYfrwZerZFT2MU4
Dl5uyQxX2KyPzFu0ZUN14NDlMBuhRANCAARgBxgXvWsvWIPoxA8R1G+2A2D6YGtE
NmE2ZTAeNXHJ+fWzitjOLrwaHKwl42/9sXfAaQP3WWYcEdSKicNrXTH3
-----END PRIVATE KEY-----

并且返回的enrollment对象内容为:

{ key: 
   ECDSA_KEY {
     _key: 
      { type: 'EC',
        isPrivate: true,
        isPublic: false,
        getBigRandom: [Function],
        setNamedCurve: [Function],
        setPrivateKeyHex: [Function],
        setPublicKeyHex: [Function],
        getPublicKeyXYHex: [Function],
        getShortNISTPCurveName: [Function],
        generateKeyPairHex: [Function],
        signWithMessageHash: [Function],
        signHex: [Function],
        sign: [Function],
        verifyWithMessageHash: [Function],
        verifyHex: [Function],
        verify: [Function],
        verifyRaw: [Function],
        serializeSig: [Function],
        parseSig: [Function],
        parseSigCompact: [Function],
        readPKCS5PrvKeyHex: [Function],
        readPKCS8PrvKeyHex: [Function],
        readPKCS8PubKeyHex: [Function],
        readCertPubKeyHex: [Function],
        curveName: 'secp256r1',
        ecparams: [Object],
        prvKeyHex: '06a61faf065ead9153d8c5380e5e6ec90c57d8ac8fcc5bb4654375e0d0e5301b',
        pubKeyHex: '0460071817bd6b2f5883e8c40f11d46fb60360fa606b4436613665301e3571c9f9f5b38ad8ce2ebc1a1cac25e36ffdb177c06903f759661c11d48a89c36b5d31f7' } },
  certificate: '-----BEGIN CERTIFICATE-----\nMIIB6jCCAZGgAwIBAgIUbwp/ziKtelTXH+F+t+KUPUp2H6gwCgYIKoZIzj0EAwIw\naDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK\nEwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMt\nY2Etc2VydmVyMB4XDTE4MDcxMzAyMTYwMFoXDTE5MDcxMzAyMjEwMFowITEPMA0G\nA1UECxMGY2xpZW50MQ4wDAYDVQQDEwVhZG1pbjBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABGAHGBe9ay9Yg+jEDxHUb7YDYPpga0Q2YTZlMB41ccn59bOK2M4uvBoc\nrCXjb/2xd8BpA/dZZhwR1IqJw2tdMfejYDBeMA4GA1UdDwEB/wQEAwIHgDAMBgNV\nHRMBAf8EAjAAMB0GA1UdDgQWBBSVRkLelRKZo4YdwGsdCouXu4KkzjAfBgNVHSME\nGDAWgBSh1y67zVpnp1sh3kHj2x4mD5Rx4zAKBggqhkjOPQQDAgNHADBEAiAU3UEg\nx4q+vkWJRKjzXgzvZWUhAaAbHihFfjHmBmqKUQIgXDZfiLVVHkfOTIBoAIvTp2Ek\n8KVd0T9j8mlU7Sj3T7k=\n-----END CERTIFICATE-----\n',
  rootCertificate: '-----BEGIN CERTIFICATE-----\nMIICFzCCAb2gAwIBAgIUdjYbUaxnBoP/JrvomKxCZuwVeDQwCgYIKoZIzj0EAwIw\naDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK\nEwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMt\nY2Etc2VydmVyMB4XDTE4MDcxMDA2NTQwMFoXDTMzMDcwNjA2NTQwMFowaDELMAkG\nA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKEwtIeXBl\ncmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMtY2Etc2Vy\ndmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESzbX2rdDUJWHu+K0C91A1Ukh\np/AIhEUp6J5SrxqrLJIh8bEv8Kv722Yj05qvVwFNi/0iRoCCNBM7XNMrpwqkEKNF\nMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE\nFKHXLrvNWmenWyHeQePbHiYPlHHjMAoGCCqGSM49BAMCA0gAMEUCIQCoCz/1dpIt\nGYozrxTcBrgsEfGCkkN9JsSr5BtSGPoCMQIgbVBTgXyPGOBf4jk2Mo9DK4zZIrkF\n56GY03W/ZiZY6Zg=\n-----END CERTIFICATE-----\n' }

3 register

fabric_client.createUser(
              {username: 'admin',
                  mspid: 'Org1MSP',
                  cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
              });
        }).then((user) => {
//
        });

执行结果:

ThinkPad-L430:/opt/fabric-samples/fabcar$ ls hfc-key-store/
2124ac635ac52a4c5d13c32812768ebe6837ae288bfa351c3712b9160f502214-priv  admin
2124ac635ac52a4c5d13c32812768ebe6837ae288bfa351c3712b9160f502214-pub
ThinkPad-L430:/opt/fabric-samples/fabcar$ cat hfc-key-store/2124ac635ac52a4c5d13c32812768ebe6837ae288bfa351c3712b9160f502214-pub 
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnUNKr708UFaMWRHbdfcM8oTW130U
H/u7X3tH+/88RQR5O8sA5EyKsMMi5OgrhwQkRbYZ8FUIiZuHRZUxL9as1w==
-----END PUBLIC KEY-----
ThinkPad-L430:/opt/fabric-samples/fabcar$ cat hfc-key-store/admin 
{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"2124ac635ac52a4c5d13c32812768ebe6837ae288bfa351c3712b9160f502214","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIIB6zCCAZGgAwIBAgIUMR8qzOAWUEfAzYlvCzQ4yTL7AHAwCgYIKoZIzj0EAwIw\naDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK\nEwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMt\nY2Etc2VydmVyMB4XDTE4MDcxMzAyMjUwMFoXDTE5MDcxMzAyMzAwMFowITEPMA0G\nA1UECxMGY2xpZW50MQ4wDAYDVQQDEwVhZG1pbjBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABJ1DSq+9PFBWjFkR23X3DPKE1td9FB/7u197R/v/PEUEeTvLAORMirDD\nIuToK4cEJEW2GfBVCImbh0WVMS/WrNejYDBeMA4GA1UdDwEB/wQEAwIHgDAMBgNV\nHRMBAf8EAjAAMB0GA1UdDgQWBBSvDlI5ax6zbDAGj3wq+4N9EF8iwDAfBgNVHSME\nGDAWgBSh1y67zVpnp1sh3kHj2x4mD5Rx4zAKBggqhkjOPQQDAgNIADBFAiEAgzdL\n6GWqvEtD03uushzYHzPUkTf+CdSISutr1hCQunkCIBY+LlllnXtW5iFnEcktP5RH\nnPmYhhiKe9DqeDw3H3ku\n-----END CERTIFICATE-----\n"}}}

这一步执行成功后, 需要设置fabric_client中的当前用户。

fabric_client.setUserContext(admin_user);

此刻, reroll整个过程完成。

© 著作权归作者所有

共有 人打赏支持
十一月不远

十一月不远

粉丝 35
博文 78
码字总数 61436
作品 1
海淀
程序员
Hyperledger Fabric 客户端开发一

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

十一月不远
07/23
0
0
IBM Blockchain 101:开发人员快速入门指南

IBM Blockchain 101:开发人员快速入门指南 如何设置区块链网络并开始编码 Nikhil Gupta 和Joshua Horton 2017 年 2 月 14 日发布 在 IBM Cloud 上进行开发-- 免费试用 30 天 您可以获得 2G...

Joshua Horton
2017/02/14
0
0
HyperLedger Fabric 学习思路分享

HyperLedger Fabric 学习思路分享     HyperLedger Fabric最初是由Digital Asset和IBM公司贡献的、由Linux基金会主办的一个超级账本项目,它是一个目前非常流行并广为人知的区块链网络框...

Aberic
06/14
0
0
Hyperledger Fabric 客户端开发二

由于知乎文章字数限制, 接上篇 , 继续介绍Hyperledger Fabric 的客户端开发。 上篇文章中, 主要介绍了Hyperledger Fabric 的 SDK相关的知识, 其中, 重点介绍了Node SDK的功能, 并通过一...

十一月不远
07/23
0
0
Hyperledger Fabric 客户端开发六

前面介绍了Fabric Chaincode的查询, 现在介绍 Chaincode的Invoke 功能, 主要包括create, update, delete功能。 完整的实例代码如下: 这段代码主要功能有两个, 一个是提交交易, 一个是获...

十一月不远
07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vue+element-ui操作删除(单行和批量删除)

页面展示: <template><!-- 表格内容 --><el-table :data="packData" border style="width: 100%" ref="multipleTable" @selection-change="handleSelectionChange"><el-tab......

琴妹
14分钟前
0
0
基于vue(element ui) + ssm + shiro 的权限框架

zhcc 基于vue(element ui) + ssm + shiro 的权限框架 引言 心声 现在的Java世界,各种资源很丰富,不得不说,从分布式,服务化,orm,再到前端控制,权限等等玲琅满目,网上有句话说,语言框架...

DarrenHu_吴邪
21分钟前
2
1
数据库水平切分(MyCat分片)

范围分片 io.mycat.route.function.AutoPartitionByLong 自动范围分片 Function名称:rang-long(配置文件默认) 枚举分片 io.mycat.route.function.PartitionByFileMap 枚举分片 Funtion名称...

这很耳东先生
22分钟前
0
0
读《HeadFirst设计模式》笔记之外观模式

外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。 举个栗子: 建了一个家庭影院,但是每次享受家庭影院时,你发现需要执行 将灯...

suyain
23分钟前
0
0
MongoDB分片配置

简单注解: mongos 路由进程, 应用程序接入mongos再查询到具体分片,监听端口默认27017 config server 路由表服务, 每一台都具有全部chunk的路由信息 shard为数据存储分片, 每一片都可以是...

LUIS1983
31分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部