• -------------------------------------------------------------
  • ====================================

Hyperleger-Fabric调用SDK和Fabric-ca-client的各种报错和解决方案

区块链 dewbay 4年前 (2020-09-29) 2442次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

99.fabric-ca-client 连接服务器报错(solved)

问题描述

对于开启了 TLS 的 ca 服务器来说。如果使用 http 方式,会报如下错误:

fabric-ca-client getcainfo -u http://admin:adminpw@localhost:7054
2019/12/11 15:42:53 [INFO] Configuration file location: /Users/liyi/github/fabric-ca/clients/admin/fabric-ca-client-config.yaml
Error: Failed to parse response: Client sent an HTTP request to an HTTPS server.
: invalid character 'C' looking for beginning of valu

对于开启了 TLS 的 ca 服务器来说,如果使用 Https 方式,但是不给证书。会报如下错误:

fabric-ca-client getcainfo -u https://admin:adminpw@localhost:7054
2019/12/11 15:47:07 [INFO] Configuration file location: /Users/liyi/github/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2019/12/11 15:47:07 [INFO] TLS Enabled
Error: Failed to get client TLS config: No trusted root certificates for TLS were provided
  • 解决
    first-network 实例中使用的是 tls 方式链接,因此必须配置–tls.certfiles 证书。对于这个例子来说,cryptogen generate –config=./crypto-config.yaml 生成,存储在 crypto-config 中。

fabric-ca-client enroll -u https://admin:adminpw@ca.org1.example.com:7054 --tls.certfiles /Users/xxxx/github/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem

1.创建用户报错 SSL

问题描述

大概上周一周的时间都在调这个 bug,今天总算知道原因了,先把情况列出来,首先是使用了Fabric官网的 Node SDK 例子balance_transfer,在第一步发请求注册的时候出现的问题

  • 发送请求

$ curl -s -X POST http://localhost:4000/users -H "content-type: application/x-www-form-urlencoded" -d 'username=Jim&orgName=Org1'

{"success":false,"message":"failed Error: Calling enrollment endpoint failed with error [Error: write EPROTO 140735804384128:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:827:\n]"}%
  • 使用单步调试,最终错误定位到了/balance-transfer/node_modules/fabric-ca-client/lib/FabricCAClientImpl.js的 844 行代码上,代码段如下:

var request = self._httpClient.request(requestOptions, function (response) {
    const responseBody = [];
    response.on('data', function (chunk) {
        responseBody.push(chunk);
    });

    response.on('end', function () {

        var payload = responseBody.join('');

        if (!payload) {
            reject(new Error(
                util.format('Enrollment failed with HTTP status code ', response.statusCode)));
        }
        //response should be JSON
        try {
            var res = JSON.parse(payload);
            if (res.success) {
                //we want the result field which is Base64-encoded PEM
                var enrollResponse = new Object();
                // Cert field is Base64-encoded PEM
                enrollResponse.enrollmentCert = Buffer.from(res.result.Cert, 'base64').toString();
                enrollResponse.caCertChain = Buffer.from(res.result.ServerInfo.CAChain, 'base64').toString();
                return resolve(enrollResponse);
            } else {
                return reject(new Error(
                    util.format('Enrollment failed with errors [%s]', JSON.stringify(res.errors))));
            }

        } catch (err) {
            reject(new Error(
                util.format('Could not parse enrollment response [%s] as JSON due to error [%s]', payload, err)));
        }
    });
});

request.on('error', function (err) {
reject(new Error(util.format('Calling enrollment endpoint failed with error [%s]', err)));
});

let body = JSON.stringify(enrollRequest);
request.write(body);
request.end();
  • 其中_httpClient值为https,也就是 nodejs 自带的模块,通过require('https')引入的,也就是相当于在发送 https 请求的时候报错
  • 该错误在网上搜了很久,也用了 github 上很多人提到的解决方案,但是并不奏效,最后发现是在 fabric 的基本 docker-compose.yaml 配置文件和 base.yaml 配置文件中有相关配置
  • 也就是TLS_ENABLED相关字段的配置,被配置成了 false,尤其是 CA 部分的,会导致存在 https 无法请求的问题,换成 http 就成功了

解决方案

  • 修改network_config.yaml文件
  • certificateAuthories处的 ca 都由https配置成http的,并且打开httpOptions配置为true,配置修改如下

certificateAuthorities:
  ca0.chainplaza.com:
    url: http://localhost:7054
    # the properties specified under this object are passed to the 'http' client verbatim when
    # making the request to the Fabric-CA server
    httpOptions:
      verify: true
    tlsCACerts:
      path: ../network/crypto-config/peerOrganizations/org0.chainplaza.com/ca/ca.org0.chainplaza.com-cert.pem

    # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
    # needed to enroll and invoke new users.
    registrar:
      - enrollId: admin
        enrollSecret: adminpw
    # [Optional] The optional name of the CA.
    caName: ca0.chainplaza.com

  ca1.chainplaza.com:
    url: http://localhost:8054
    httpOptions:
      verify: true
    tlsCACerts:
      path: ../network/crypto-config/peerOrganizations/org1.chainplaza.com/ca/ca.org1.chainplaza.com-cert.pem
    registrar:
      - enrollId: admin
        enrollSecret: adminpw
    # [Optional] The optional name of the CA.
    caName: ca1.chainplaza.com

  ca2.chainplaza.com:
    url: http://localhost:9054
    httpOptions:
      verify: true
    tlsCACerts:
      path: ../network/crypto-config/peerOrganizations/org2.chainplaza.com/ca/ca.org2.chainplaza.com-cert.pem
    registrar:
      - enrollId: admin
        enrollSecret: adminpw
    # [Optional] The optional name of the CA.
    caName: ca2.chainplaza.com

2.创建 channel 报错 SSL

问题描述

  • 首先基于第一步成功注册用户,获取 token,执行

$ token=<put JSON Web Token here>
  • 发送请求

$ curl -s -X POST \
  http://localhost:4000/channels \
  -H "authorization: Bearer $token" \
  -H "content-type: application/json" \
  -d '{
    "channelName":"mychannel",
    "channelConfigPath":"../artifacts/channel/mychannel.tx"
}'

E0702 16:48:37.113124000 140735804384128 ssl_transport_security.c:921] Handshake failed with fatal error SSL_ERROR_SSL: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number.
E0702 16:48:37.117412000 140735804384128 ssl_transport_security.c:921] Handshake failed with fatal error SSL_ERROR_SSL: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number.
error: [Orderer.js]: sendBroadcast - on error: "Error: Connect Failed\n    at ClientDuplexStream._emitStatusIfDone (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:255:19)\n    at ClientDuplexStream._readsDone (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:221:8)\n    at readCallback (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:283:12)"
[2018-07-02 16:48:37.124] [ERROR] Create-Channel - Error: SERVICE_UNAVAILABLE
    at ClientDuplexStream.<anonymous> (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/fabric-client/lib/Orderer.js:136:21)
    at emitOne (events.js:116:13)
    at ClientDuplexStream.emit (events.js:211:7)
    at ClientDuplexStream._emitStatusIfDone (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:258:12)
    at ClientDuplexStream._readsDone (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:221:8)
    at readCallback (/Users/guanpengchn/Code/chain_plaza/balance-transfer/node_modules/grpc/src/node/src/client.js:283:12)
(node:28483) UnhandledPromiseRejectionWarning: Error: Failed to initialize the channel: Error: SERVICE_UNAVAILABLE
    at Object.createChannel (/Users/guanpengchn/Code/chain_plaza/balance-transfer/app/create-channel.js:65:9)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:28483) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing insideof an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:28483) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
E0702 16:48:57.113731000 140735804384128 ssl_transport_security.c:921] Handshake failed with fatal error SSL_ERROR_SSL: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number.
E0702 16:48:57.120118000 140735804384128 ssl_transport_security.c:921] Handshake failed with fatal error SSL_ERROR_SSL: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number.
  • 将错误定位到了 orderer 节点的 broadcast 的函数上,根据错误提示,其实本质上也是由于协议错误导致的,根据 github 上的一个回答,使用 http 协议时不要用 grpcs,而要用 grpc

解决方案

3.创建用户报错 Failed to get Affiliation

问题描述

  • 注册用户发送请求

$ curl -s -X POST http://localhost:4000/users -H "content-type: application/x-www-form-urlencoded" -d 'username=Jim&orgName=Org0'

{"success":false,"message":"failed Error: fabric-ca request register failed with errors [[{\"code\":63,\"message\":\"Failed to get Affiliation: sql: no rows in result set\"}]]"}%
  • 获取到资料表示说,fabric-ca 只有org1.department1 org1.department2 org2.department1,所以此处使用 Org0 报错

解决方案

  • 在命令行执行如下语句,其中fabric-ca-clientfabric-samples/bin中,其中 7054 是 ca0 的端口,相对应关系

$ fabric-ca-client enroll -u http://admin:adminpw@localhost:7054
2018/07/02 17:51:00 [INFO] Created a default configuration file at /Users/guanpengchn/.fabric-ca-client/fabric-ca-client-config.yaml
2018/07/02 17:51:00 [INFO] generating key: &{A:ecdsa S:256}
2018/07/02 17:51:00 [INFO] encoded CSR
2018/07/02 17:51:00 [INFO] Stored client certificate at /Users/guanpengchn/.fabric-ca-client/msp/signcerts/cert.pem
2018/07/02 17:51:00 [INFO] Stored root CA certificate at /Users/guanpengchn/.fabric-ca-client/msp/cacerts/localhost-7054.pem
2018/07/02 17:51:00 [INFO] Stored intermediate CA certificates at /Users/guanpengchn/.fabric-ca-client/msp/intermediatecerts/localhost-7054.pem

$ fabric-ca-client affiliation add org0
2018/07/02 17:51:09 [INFO] Configuration file location: /Users/guanpengchn/.fabric-ca-client/fabric-ca-client-config.yaml
Successfully added affiliation: org0

$ fabric-ca-client affiliation add org0.department1
2018/07/02 17:51:15 [INFO] Configuration file location: /Users/guanpengchn/.fabric-ca-client/fabric-ca-client-config.yaml
Successfully added affiliation: org0.department1

这时候再注册就成功了

4、create Affiliation 的 rest 接口实现

问题描述

  • 之前通过命令行的方式能够成功添加 affiliation,但是并不方便有缺陷,balance_transfer 中的例子又没有做相关工作,倒是在 fabcar 中有使用,但虽然目的一样,实际上实现有很大差别,无法使用

解决方案

  • 于是通过 debug 后发现helper.jsgetRegisteredUser函数中,在 enroll 了 admin 之后的 caClient 变量有 newAffiliationService 函数
  • 仿照 fabcar 中的调用方式和 helper.js 的getRegisteredUser函数写法,做了如下实现

helper.js添加代码如下

var createAffiliation = async function(affiliationName) {
    try {
        var client = await getClientForOrg(affiliationName);
        logger.debug('Successfully initialized the credential stores');
        var admins = hfc.getConfigSetting('admins');
        let adminUserObj = await client.setUserContext({username: admins[0].username, password: admins[0].secret});
        let caClient = client.getCertificateAuthority();
        let tmp = await caClient.newAffiliationService().create({ name: affiliationName }, adminUserObj );
        logger.debug('Successfully create affiliation');
        var response = {
            success: true,
            message: affiliationName + ' create Successfully',
        };
        return response;
    } catch(error) {
        logger.error('Failed to create affiliation with error: %s', error.toString());
        logger.error(error.stack.toString());
        return 'failed '+error.toString();
    }

};

app.js中添加代码如下

// Create Affiliation
app.post('/affiliations', async function(req, res) {
    logger.info('<<<<<<<<<<<<<<<<< C R E A T E  A F F I L I A T I O N >>>>>>>>>>>>>>>>>');
    logger.debug('End point : /affiliation');
    var orgName = req.body.orgName;
    logger.debug('Affiliation name : ' + orgName);
    if (!orgName) {
        res.json(getErrorMessage('\'orgName\''));
        return;
    }

    let response = await helper.createAffiliation(orgName);

    if (response && typeof response !== 'string') {
        logger.debug('Successfully create affiliation %s ',orgName);
        res.json(response);
    } else {
        logger.debug('Failed to create affiliation %s with::%s',orgName,response);
        res.json({success: false, message: response});
    }
});

这样实现了 rest 接口,能够 create affiliation,执行

$ fabric-ca-client affiliation list

即可看添加结果,最后一行

[图片上传失败…(image-8d2604-1575981368999)]

5、使用 balance-transfer 遇到无效网络配置

问题描述

Error: Invalid network configuration due to missing configuration data

解决方案

  • 原因就是在 artifacts 下的 yaml 文件缺失,所以找不到,涉及到的文件为artifacts/network-config.yaml,artifacts/org1.yaml,artifacts/org2.yaml等,文件所在目录
  • 其中在 config.js 文件中配有文件查询的代码

var util = require('util');
var path = require('path');
var hfc = require('fabric-client');

var file = 'network-config%s.yaml';

var env = process.env.TARGET_NETWORK;
if (env)
    file = util.format(file, '-' + env);
else
    file = util.format(file, '');
// indicate to the application where the setup file is located so it able
// to have the hfc load it to initalize the fabric client instance
hfc.setConfigSetting('network-connection-profile-path',path.join(__dirname, 'artifacts' ,file));
hfc.setConfigSetting('Org1-connection-profile-path',path.join(__dirname, 'artifacts', 'org1.yaml'));
hfc.setConfigSetting('Org2-connection-profile-path',path.join(__dirname, 'artifacts', 'org2.yaml'));
// some other settings the application might need to know
hfc.addConfigFile(path.join(__dirname, 'config.json'));

  • 其中要特别指出的是,在所有 rest 接口传入的 orgName 的变量都是根据'Org1-connection-profile-path'中的前面Org1来做文件查找的,在代码里体现就是

代码位置

// get a fabric client loaded with a connection profile for this org
let config = '-connection-profile-path';

// build a client context and load it with a connection profile
// lets only load the network settings and save the client for later
let client = hfc.loadFromConfig(hfc.getConfigSetting('network'+config));

6、create Affiliation 后依然报错 Failed to get Affiliation

问题描述

  • 在添加好上述 rest 接口之后,做了 rest 请求并且成功添加了 org0,但是下一步注册依然报错

解决方案

  • 原因在于在注册时被代码字符串连接了一个 department1,所以只添加了 org0 是无法注册的,要添加 org0.department1 才可以,但是 rest 方式无法做到,所以我就删掉了后面的字符串拼接,原代码

let secret = await caClient.register({
    enrollmentID: username,
    affiliation: userOrg.toLowerCase() + '.department1'
}, adminUserObj);

修改后的代码

let secret = await caClient.register({
enrollmentID: username,
affiliation: userOrg.toLowerCase()
}, adminUserObj);

7、instantiate chaincode 时 args 参数为空报错

问题描述

  • 在发 instantiate chaincode 的 http 请求中包含如下参数

{
    "peers": ["peer4.org2.chainplaza.com"],
    "chaincodeName":"hospital2",
    "chaincodeVersion":"0",
    "chaincodeType": "golang",
    "args": []
}

其中 args:[]这里参数为空,结果报错

"Incorrect arguments. Expecting no initial arguments"

解决方案

  • 该错误是我们写的 chaincode 中的报错,代码如下:

args := stub.GetStringArgs()
if len(args) != 0 {
    return shim.Error("Incorrect arguments. Expecting no initial arguments")
}

const args = [];
//args.push(Buffer.from(request.fcn ? request.fcn : 'init', 'utf8'));
  • 不注释掉就会导致 chaincode 一侧需要 0 个参数,而存在 LSCC 操作在真正的 instantiate 前面,但 LSCC 因为上面那行代码却有一个参数,故而报错,代操作代码如下

        /*
     * Internal method to handle both chaincode calls
     */
    _sendChaincodeProposal(request, command, timeout) {
        let errorMsg = null;

        //validate the incoming request
        if (!errorMsg) errorMsg = clientUtils.checkProposalRequest(request);
        if (!errorMsg) errorMsg = clientUtils.checkInstallRequest(request);
        if (errorMsg) {
            logger.error('sendChainCodeProposal error ' + errorMsg);
            return Promise.reject(new Error(errorMsg));
        }
        const peers = this._getTargets(request.targets, Constants.NetworkConfig.ENDORSING_PEER_ROLE);

        // args is optional because some chaincode may not need any input parameters during initialization
        if (!request.args) {
            request.args = [];
        }

        // step 1: construct a ChaincodeSpec
        const args = [];
        //args.push(Buffer.from(request.fcn ? request.fcn : 'init', 'utf8'));

        for (let arg of request.args)
            args.push(Buffer.from(arg, 'utf8'));

        const ccSpec = {
            type: clientUtils.translateCCType(request.chaincodeType),
            chaincode_id: {
                name: request.chaincodeId,
                version: request.chaincodeVersion
            },
            input: {
                args: args
            }
        };

        // step 2: construct the ChaincodeDeploymentSpec
        const chaincodeDeploymentSpec = new _ccProto.ChaincodeDeploymentSpec();
        chaincodeDeploymentSpec.setChaincodeSpec(ccSpec);

        const signer = this._clientContext._getSigningIdentity(request.txId.isAdmin());
        const lcccSpec_args = [
            Buffer.from(command),
            Buffer.from(this._name),
            chaincodeDeploymentSpec.toBuffer()
        ];
        if (request['endorsement-policy']) {
            lcccSpec_args[3] = this._buildEndorsementPolicy(request['endorsement-policy']);
        }

        const lcccSpec = {
            // type: _ccProto.ChaincodeSpec.Type.GOLANG,
            type: clientUtils.translateCCType(request.chaincodeType),
            chaincode_id: { name: Constants.LSCC },
            input: { args: lcccSpec_args }
        };

        const channelHeader = clientUtils.buildChannelHeader(
            _commonProto.HeaderType.ENDORSER_TRANSACTION,
            this._name,
            request.txId.getTransactionID(),
            null,
            Constants.LSCC,
            clientUtils.buildCurrentTimestamp(),
            peers[0].getClientCertHash()
        );
        const header = clientUtils.buildHeader(signer, channelHeader, request.txId.getNonce());
        const proposal = clientUtils.buildProposal(lcccSpec, header, request.transientMap);
        const signed_proposal = clientUtils.signProposal(signer, proposal);

        return clientUtils.sendPeersProposal(peers, signed_proposal, timeout)
            .then(
                function (responses) {
                    return [responses, proposal];
                }
            );
    }

露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Hyperleger-Fabric调用SDK和Fabric-ca-client的各种报错和解决方案
喜欢 (0)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址