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

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:[email protected]: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=
  • 发送请求
$ 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. (/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 
    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

解决方案

  • 修改network_config.yaml文件

  • 将所有grpcs配置成grpc然后创建channel就成功了

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")
}
  • 注释掉fabric-sdk-node中Channel.js的一行代码
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];
                }
            );
    }

你可能感兴趣的:(Hyperleger-Fabric调用SDK和Fabric-ca-client的各种报错和解决方案)