HyperLedger示例部署和简单合约开发问题总结

背景

HyperLedger从2015年12月发起,经过多年的发展,已经日渐成熟,到目前为止已经成为区块链领域企业级产品开发首选技术框架,虽然关于Hyperledger底层架构能否可以被称作真正的区块链技术,网络上有些异议。作为一个开发人员,如果要涉足区块链开发领域,HyperLedger是一个必须了解的技术框架。

今日,我在自己的Mac上部署并搭建了一套完整的HyperLedger区块链网络,然后开发了一个简单的智能合约,部署到自定义的一个HyperLedger Network上,虽然HyperLedger中的众多模块都是基于Docker做部署,但是由于HyperLedger架构存在众多的角色,整个网络如果想要完整运行,依赖多个配置文件,如果没有理解了架构中的组件,想要搭建一个自定义的Network,而不是仅限于跑一下官方的demo那么简单,中间还是会遇到很多问题,这篇文件,简单总结了我在部署和运行过程中遇到的一些问题。

本地部署流程

这是官方文档中Commercial Paper示例的一个交互图,在了解了基本的概念后,还需要知道在HyperLedger Fabric Network 中完成一笔交易需要哪些角色,整个流程是怎么进行的。

commercialpaper.application

我在本地部署的过程中除了部署官方demo中的Fabcar,Commercial Paper示例,还自己尝试着修改并搭建了一个简单的自定义fabric network,并部署了自己编写的合约。主要做了以下工作

  1. 参考fabic-sample目录下的first-network配置,创建了包含两个新的Org的network,涉及到了docker-compose-base.yaml,configtx.yraml,crypto-config.yaml等配置文件,其中configtx.yaml文件用于配置,crypto-config.yaml文件用于在crypto-config生成Org,Peer,Orderer等配置文件,configtx.yraml文件用于在chanel-artifacts目录下完成genesis block创建和channel 初始化等交易;以上动作都是在byfn.sh脚本中执行完成,我们只需要给出正确的配置文件即可。
  2. 参考fabcar的startFabric.sh文件,创建新network的启动脚本,修改脚本文件中docker相关参数,保证org,peer,mspId等与上一步的配置一致,否则可能会启动失败。
  3. 编写自己的合约,部署到fabic-sample/chaincode目录下,这个目录是在startFabric.sh中CC_SRC_PATH指定的,如果部署到了非当前目录,请确保volumes配置能争取映射
  4. 执行startFabric.sh,启动fabric network,install和instantiate chaincode,执行initLedger合约初始化函数调用,也可以通过peer chaincode invoke执行其他合约函数调用
  5. 参考fabcar java application代码,编写java application代码,接入到fabric network并执行合约函数的调用,测试合约是否正常运行
  6. 部署hyperLedger blockchain-explorer,参考官方github里面的部署过程,修改app/platform/fabric/connection-profile/connection-profile.json,确保channel和organizations,peers,certificateAuthorities配置
  7. 通过java application代码,执行合约函数调用,发起交易修改区块链上数据
  8. 通过hyperLedger blockchain-explorer查看链上数据

问题总结

其中在部署chaincode,执行合约调用,以及部署blockchain-explorer过程中会遇到很多问题,大部分都是由于配置缺失或者环境问题导致,下面是我遇到的一些难以解决的问题总结。

  1. grpc和http地址错误

    1
    2
    3
    getConfigBlock for channel mychannel failed with peer peer0.dwd.example.com.  Status FAILURE, details: Channel Channel{id: 5, name: dwd-channel} Sending proposal with transaction: c44ce1fad75cd6305cfe9d4540373d6100e78e3fa5c8363a4e2ff91f3486c7c4 to Peer{ id: 6, name: peer0.dwd.example.com, channelName: dwd-channel, url: grpc://localhost:7051, mspid: DwdMSP} failed because of: gRPC failure=Status{code=INTERNAL, description=http2 exception, cause=io.netty.handler.codec.http2.Http2Exception: First received frame was not SETTINGS. Hex dump for first 5 bytes: 1503010002

    TLS handshake failed with error tls: first record does not look like a TLS handshake server=PeerServer remoteaddress=172.23.0.1:43812

    解决方案:修改原配置文件中peers,org等节点下的的grpc和http协议为grpcs和https

  2. blockchain-explorer无法连接fabric网络

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    2019-11-08T05:54:29.319Z - error: [FabricCAClientService.js]: Invalid enroll request, missing enrollmentID
    'Error instantiating FabricCAServices '
    2019-11-08T05:54:29.345Z - error: [Channel.js]: Error: No identity has been assigned to this client
    .......


    ************************************ initializeDetachClient *************************************************
    Error : Failed to connect client peer, please check the configuration and peer status
    Info : Explorer will continue working with only DB data
    ************************************** initializeDetachClient ************************************************

    FabricUtils.createDetachClient


    Please open web browser to access :http://localhost:8080/


    pid is 23715


    FabricConfig, this.config.channels dwd-channel
    <<<<<<<<<<<<<<<<<<<<<<<<<< Explorer Error >>>>>>>>>>>>>>>>>>>>>
    Error : [ 'Default client peer is down and no channel details available database' ]

    解决方案:需要在Organizations下面配置正确的adminPrivateKey和signedCert,例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    "organizations": {
    "Org1MSP": {
    "mspid": "Org1MSP",
    "fullpath": true,
    "adminPrivateKey": {
    "path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/b8dbe986004cef4382c345ff9886d4e3d115ee7fb6344ef316dd7c9397933698_sk"
    },
    "signedCert": {
    "path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/first-network/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem"
    }
    }
    }
  3. node版本错误导致blockchain-explorer启动失败

    1
    2
    3
    { Error: Failed to load gRPC binary module because it was not installed for the current system
    Expected directory: node-v57-linux-x64-glibc
    Found: [node-v64-linux-x64-glibc]

    解决方案:blockchain-explorer文档中要求node版本是8.11.x,所以需要安装nvm,并修改当前node版本为8.11.x

  4. peer节点未全部启动导致合约函数执行失败

    1
    ENDORSEMENT_POLICY_FAILURE error on on invocation of chaincode functions

    解决方案:查看在执行peer chaincode instantiate 时候Endorsement Policy,如果是配置了多个peer,例如 -P “AND (‘Org1MSP.peer’,’Org2MSP.peer’) ,可以通过docker ps命令Org1和Org2和peer节点正常启动。因为peer chaincode instantiate 执行需要一段时间,有时候会卡住,导致未启动所有peer节点,最后交易得到无法验证

  5. java 合约部署失败

    1
    stat /root/chaincode-java/start: no such file or directory

    目前未解决,所以我后面合约用javaScript编写

两个配置文件

除了以上问题,还有其他一些因为配置问题出错导致的异常,为了便于定位问题,可以通过在docker命令中增加-e FABRIC_LOGGING_SPEC=DEBUG参数来修改日志级别。另外也可以通过commerial-paper示例中的monitordocker.sh 文件来查看所有docker container的日志

另外,在通过java调用合约函数过程中发现,fabcar示例中的network-config.yml和connection.json文件缺少某些配置信息,导致无法invoke 合约函数,java application中fabric-gateway-java 依赖版本是1.4.0,下面给出测试成功的network-config.yml和connection.json配置。

network-config.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#network-config.yml配置
name: test-network
version: 1.0.0


channels:
# name of the channel
test-channel:

client:
organization: TestOrg1
connection:
timeout:
peer:
endorser: '300'
organizations:
TestOrg1:
mspid: TestOrg1MSP
peers:
- peer0.testOrg1.example.com
- peer1.testOrg1.example.com
certificateAuthorities:
- ca.testOrg1.example.com
TestOrg2:
mspid: TestOrg2MSP
peers:
- peer0.testOrg2.example.com
- peer1.testOrg2.example.com
certificateAuthorities:
- ca.testOrg2.example.com
peers:
peer0.testOrg1.example.com:
url: grpcs://127.0.0.1:7051
tlsCACerts:
pem: |

grpcOptions:
ssl-target-name-override: peer0.testOrg1.example.com
hostnameOverride: peer0.testOrg1.example.com
peer1.testOrg1.example.com:
url: grpcs://127.0.0.1:8051
tlsCACerts:
pem: |

grpcOptions:
ssl-target-name-override: peer1.testOrg1.example.com
hostnameOverride: peer1.testOrg1.example.com
peer0.testOrg2.example.com:
url: grpcs://127.0.0.1:7056
tlsCACerts:
pem: |

grpcOptions:
ssl-target-name-override: peer0.testOrg2.example.com
hostnameOverride: peer0.testOrg2.example.com
peer1.testOrg2.example.com:
url: grpcs://127.0.0.1:8056
tlsCACerts:
pem: |

grpcOptions:
ssl-target-name-override: peer1.testOrg2.example.com
hostnameOverride: peer1.testOrg2.example.com
certificateAuthorities:
ca.testOrg1.example.com:
url: https://127.0.0.1:7054
caName: ca-testOrg1
tlsCACerts:
pem: |

httpOptions:
verify: false

ca.testOrg2.example.com:
url: http://127.0.0.1:8054
caName: ca-testOrg2
tlsCACerts:
pem: |

httpOptions:
verify: false

connection.json 配置文件(blockchain-explorer配置文件与此文件类似)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{
"name": "test-network",
"version": "1.0.0",
"client": {
"tlsEnable": true,
"adminUser": "admin",
"adminPassword": "adminpw",
"enableAuthentication": false,
"organization": "TestOrg1",
"connection": {
"timeout": {
"peer": {
"endorser": "300"
},
"orderer": "300"
}
}
},
"channels":{
"test-channel": {
"orderers": ["orderer.example.com"],
"peers": {
"peer0.testOrg1.example.com": {},
"peer1.testOrg1.example.com": {},
"peer0.testOrg2.example.com": {},
"peer1.testOrg2.example.com": {}
}
}
},
"orderers":{
"orderer.example.com": {
"url": "grpcs://127.0.0.1:7050",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem"
},
"grpcOptions": {
"ssl-target-name-override": "orderer.example.com",
"hostnameOverride": "orderer.example.com"
}
}
},
"client": {
"organization": "TestOrg1",
"connection": {
"timeout": {
"peer": {
"endorser": "300"
}
}
}
},
"organizations": {
"TestOrg1": {
"mspid": "TestOrg1MSP",
"peers": [
"peer0.testOrg1.example.com",
"peer1.testOrg1.example.com"
],
"certificateAuthorities": [
"ca.testOrg1.example.com"
]
},
"TestOrg2": {
"mspid": "TestOrg2MSP",
"peers": [
"peer0.testOrg2.example.com",
"peer1.testOrg2.example.com"
],
"certificateAuthorities": [
"ca.testOrg2.example.com"
]
}
},
"peers": {
"peer0.testOrg1.example.com": {
"url": "grpcs://127.0.0.1:7051",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg1.example.com/peers/peer0.testOrg1.example.com/msp/tlscacerts/tlsca.testOrg1.example.com-cert.pem"
},
"grpcOptions": {
"ssl-target-name-override": "peer0.testOrg1.example.com",
"hostnameOverride": "peer0.testOrg1.example.com"
}
},
"peer1.testOrg1.example.com": {
"url": "grpcs://127.0.0.1:8051",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg1.example.com/peers/peer1.testOrg1.example.com/msp/tlscacerts/tlsca.testOrg1.example.com-cert.pem"
},
"grpcOptions": {
"ssl-target-name-override": "peer1.testOrg1.example.com",
"hostnameOverride": "peer1.testOrg1.example.com"
}
},
"peer0.testOrg2.example.com": {
"url": "grpcs://127.0.0.1:9051",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg2.example.com/peers/peer0.testOrg2.example.com/msp/tlscacerts/tlsca.testOrg2.example.com-cert.pem"
},
"grpcOptions": {
"ssl-target-name-override": "peer0.testOrg2.example.com",
"hostnameOverride": "peer0.testOrg2.example.com"
}
},
"peer1.testOrg2.example.com": {
"url": "grpcs://127.0.0.1:10051",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg2.example.com/peers/peer1.testOrg2.example.com/msp/tlscacerts/tlsca.testOrg2.example.com-cert.pem"
},
"grpcOptions": {
"ssl-target-name-override": "peer1.testOrg2.example.com",
"hostnameOverride": "peer1.testOrg2.example.com"
}
}
},
"certificateAuthorities": {
"ca.testOrg1.example.com": {
"url": "https://127.0.0.1:7054",
"caName": "ca-testOrg1",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg1.example.com/tlsca/tlsca.testOrg1.example.com-cert.pem"
},
"httpOptions": {
"verify": false
}
},
"ca.testOrg2.example.com": {
"url": "https://127.0.0.1:8054",
"caName": "ca-testOrg2",
"tlsCACerts": {
"path": "/Users/jaryur/go/src/github.com/hyperledger/fabric-samples/test-network/crypto-config/peerOrganizations/testOrg2.example.com/tlsca/tlsca.testOrg2.example.com-cert.pem"
},
"httpOptions": {
"verify": false
}
}
}
}

总结

在整个学习过程中,我基本都是通过官方文档了解HyperLedger的基本架构和概念,然后通过Developing ApplicationsTutorials 部分熟悉了启动fabric网络和部署,实例化,执行合约等这些过程是怎么发生,然后自己编写自己的chaincode,java application并启动运行,在其中遇到了很多问题,也通过解决问题对HyperLedger有了更清晰的认识,但是如果要深入到底层实现原理,需要对源码进行研究。

HyperLedger和传统区块链中的Bitcoin和Ethereum在架构上都有较大的区别,因此HyperLedger更多的适用于企业级联盟链的开发,尤其是实现“无币区块链”,目前国内在政策的号召下,区块链可能会成为一个新的技术热点,但是目前相关开发人员还是相对较少,这对我们来说也是一个新的机遇。