谜之报错
下面是一个使用fabric-java-sdk和fabric-java-gateway尝试连接上述虚拟机的未解决的一堆报错。更离谱的是,每一次报错都还不一样。。。
由于这是一个未解决的连接错误,我只阐述我做了什么和得到的反馈,也希望有类似经历的伙伴提供一些建议。
connection.json配置文件
{ "name": "basic-network", "version": "1.0.0", "dependencies": { }, "client": { "organization": "Department1", "connection": { "timeout": { "peer": { "endorser": "30000" }, "orderer": "30000" } }, "organization": "Department2", "connection": { "timeout": { "peer": { "endorser": "30000" }, "orderer": "30000" } } }, "channels": { "tracechannel": { "orderers": [ "orderer.trace.com" ], "peers": { "peer0.department1.trace.com": { "endorsingPeer": true, "chaincodeQuery": true, "ledgerQuery": true, "eventSource": true }, "peer0.department2.trace.com": { "endorsingPeer": true, "chaincodeQuery": true, "ledgerQuery": true, "eventSource": true }, } } }, "organizations": { "Department1": { "mspid": "Department1MSP", "peers": [ "peer0.department1.trace.com", ], "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/department1.trace.com/users/Admin@department1.trace.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/department1.trace.com/users/Admin@department1.trace.com/msp/signcerts/Admin@department1.trace.com-cert.pem" } }, "Department2": { "mspid": "Department2MSP", "peers": [ "peer0.department2.trace.com", ], "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/department2.trace.com/users/Admin@department2.trace.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/peerOrganizations/department2.trace.com/users/Admin@department2.trace.com/msp/signcerts/Admin@department2.trace.com-cert.pem" } } }, "mspid": "OrdererMSP", "grpcOptions": {"orderers": { "orderer.trace.com": { "url": "grpcs://127.0.0.1:7050", "ssl-target-name-override": "orderer.trace.com", "hostnameOverride": "orderer.trace.com" }, "tlsCACerts": { "path": "src/main/resources/crypto-config/ordererOrganizations/trace.com/orderers/orderer.trace.com/tls/ca.crt" }, "adminPrivateKeyPEM": { "path": "src/main/resources/crypto-config/ordererOrganizations/trace.com/users/Admin@trace.com/msp/keystore/priv_sk" }, "signedCertPEM": { "path": "src/main/resources/crypto-config/ordererOrganizations/trace.com/users/Admin@trace.com/msp/signcerts/Admin@trace.com-cert.pem" } } }, "peers": { "peer0.department1.trace.com": { "url": "grpcs://127.0.0.1:7051", "grpcOptions": { "ssl-target-name-override": "peer0.department1.trace.com", "hostnameOverride": "peer0.department1.trace.com", "request-timeout": 120001 }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/department1.trace.com/peers/peer0.department1.trace.com/tls/ca.crt" } }, "peer0.department2.trace.com": { "url": "grpcs://127.0.0.1:8051", "grpcOptions": { "ssl-target-name-override": "peer0.department2.trace.com", "hostnameOverride": "peer0.department2.trace.com", "request-timeout": 120001 }, "tlsCACerts": { "path": "src/main/resources/crypto-config/peerOrganizations/department2.trace.com/peers/peer0.department2.trace.com/tls/ca.crt" } }, } }
官方案例测试Sample.java
package com.zcongfly; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.hyperledger.fabric.gateway.*; import org.hyperledger.fabric.gateway.impl.GatewayImpl; import org.hyperledger.fabric.sdk.HFClient; import java.io.IOException; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * @author admin */ public class Sample { private Gateway gateway; private Network network; private static final Path NETWORK_CONFIG_PATH = Paths.get("src","main", "resources", "connection.json"); private static final Path credentialPath = Paths.get("src","main","resources", "crypto-config", "peerOrganizations", "department1.trace.com", "users", "User1@department1.trace.com", "msp"); public static void main(String[] args) throws IOException { X509Certificate certificate = null; PrivateKey privateKey = null; Gateway gateway = null; try { //使用org1中的user1初始化一个网关wallet账户用于连接网络 Wallet wallet = Wallets.newInMemoryWallet(); Path certificatePath = credentialPath.resolve(Paths.get("signcerts", "User1@department1.trace.com-cert.pem")); certificate = readX509Certificate(certificatePath); Path privateKeyPath = credentialPath.resolve(Paths.get("keystore", "priv_sk")); privateKey = getPrivateKey(privateKeyPath); wallet.put("user",Identities.newX509Identity("Department1MSP",certificate,privateKey)); //根据connection.json 获取Fabric网络连接对象 GatewayImpl.Builder builder = (GatewayImpl.Builder) Gateway.createBuilder(); builder.identity(wallet, "user").networkConfig(NETWORK_CONFIG_PATH); //连接网关 gateway = builder.connect(); System.out.println("=====================已连接到网关!======================="); //获取mychannel通道 Network network = gateway.getNetwork("mychannel"); System.out.println(network); System.out.println("===================已连接到指定通道!========================================"); //获取合约对象 Contract myoilcc = network.getContract("myoilcc"); System.out.println(myoilcc); // 提交事务 存储到账本 //byte[] createSaccResult = myprovocoilContract.createTransaction("addoilaccount").submit("3","2022.04.21","136.0","3","1","","36"); //System.out.println("myprovocoil油料存储:" + new String(createSaccResult, StandardCharsets.UTF_8)); // 查询 //byte[] queSaccResult = myprovocoilContract.createTransaction("query").submit("001油品"); //System.out.println("myprovocoil油料查询结果:" + new String(queSaccResult, StandardCharsets.UTF_8)); } catch (Exception e) { e.printStackTrace(); } } /** * 从文件或流中读取X.509证书并返回一个X509Certificate对象。 *X.509证书是一种数字证书格式,常用于加密、签名和身份验证等安全应用程序中。 * 在Hyperledger Fabric中,X.509证书也被广泛用于身份验证和授权。 * 使用Java编写的Hyperledger Fabric客户端应用程序通常需要读取和解析X.509证书以进行身份验证和交易提交。 * readX509Certificate方法接受一个InputStream参数,该参数指定了包含X.509证书数据的输入流。 * 该方法首先创建一个CertificateFactory实例,并使用该实例从输入流中加载证书数据并返回一个X509Certificate对象。 */ private static X509Certificate readX509Certificate(final Path certificatePath) throws IOException, CertificateException { try (Reader certificateReader = Files.newBufferedReader(certificatePath, StandardCharsets.UTF_8)) { return Identities.readX509Certificate(certificateReader); } } /** * 从文件或流中读取私钥数据并返回一个PrivateKey对象。 * 在Hyperledger Fabric中,使用X.509证书进行身份验证和授权时,通常需要提供与证书关联的私钥。 * 使用Java编写的Hyperledger Fabric客户端应用程序通常需要读取和解析私钥以进行身份验证和交易提交。 * getPrivateKey方法接受两个参数,分别是包含私钥数据的InputStream对象和私钥的密码。 * 该方法首先创建一个KeyStore实例,并使用该实例从输入流中加载私钥数据和相关证书链。 * 然后,该方法使用密码解密私钥数据,并返回一个PrivateKey对象。 */ private static PrivateKey getPrivateKey(final Path privateKeyPath) throws IOException, InvalidKeyException { try (Reader privateKeyReader = Files.newBufferedReader(privateKeyPath, StandardCharsets.UTF_8)) { return Identities.readPrivateKey(privateKeyReader); } } }
报错1:
虚拟机使用的是NAT模式,已开启ssh服务,并在虚拟机上设置了端口转发,将22端口映射成主机上的2222端口,目前主机可以通过ssh命令连接到虚拟机,命令是ssh username@127.0.0.1 -p 2222,但是再使用fabric-java-sdk连接连接到该虚拟机上启动的fabric网络时一直报错,目前主机仍然无法ping通虚拟机的ip地址,只能通过ssh username@127.0.0.1 -p 2222命令与虚拟机进行连接和通信。
以下是connection.json的当前配置:
"orderers": { "orderer.trace.com": { "url": "grpcs://localhost:7050",
不论将url改成127.0.0.1、localhost、虚拟机ip、虚拟机内部IP均报错Connection refused
:
ERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: 7d61cbf1e1cbdab5a17d552254cd5957bbd19006813169c2c6031e90dfd384ae to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://localhost:8051, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=io exception, cause=io.grpc.netty.shaded.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: localhost/[0:0:0:0:0:0:0:1]:8051 Caused by: java.net.ConnectException: Connection refused: no further information
将url改为grpcs://localhost:2222,报错Connection refused
:
ERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: ca2ff093f2624894dc15a90ef9f7f497d5510fad9e3e29663a143b1609007447 to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://localhost:2222, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=io exception, cause=io.grpc.netty.shaded.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: localhost/[0:0:0:0:0:0:0:1]:2222 Caused by: java.net.ConnectException: Connection refused: no further information
这应该属于同一类错误。
报错2:
查阅资料,启用ssh隧道:
ssh -L 5000:localhost:5000 zcongfly@127.0.0.1 -p 2222
修改所有节点的grpc的url为:
"url": "grpcs://localhost:5000",
报错变成了Connection reset
:
ERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: d98f80a464b5011fa2cbee88d74a6ce8bcab09c39244bb0670ccabd2325b9347 to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://127.0.0.1:5000, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=io exception Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0], cause=java.net.SocketException: Connection reset
报错3:
进一步查阅资料:
在crypto-config.ymal中添加SANS:
SANS: - "localhost"
在Java工程的src/main/resources文件夹下以管理员身份运行cmd,输入命令:
# 添加证书到java的jvm密钥库中 keytool -import -file crypto-config\peerOrganizations\department2.trace.com\peers\peer0.department2.trace.com\tls\server.crt -alias peer0.department2.trace.com -keystore "C:\Program Files\Java\jre1.8.0_281\lib\security\cacerts" -storepass changeit keytool -import -file crypto-config\peerOrganizations\department1.trace.com\peers\peer0.department1.trace.com\tls\server.crt -alias peer0.department1.trace.com -keystore "C:\Program Files\Java\jre1.8.0_281\lib\security\cacerts" -storepass changeit keytool -import -file crypto-config\ordererOrganizations\trace.com\orderers\orderer.trace.com\tls\server.crt -alias orderer.trace.com -keystore "C:\Program Files\Java\jre1.8.0_281\lib\security\cacerts" -storepass changeit
再次运行Sample.java,报错你的主机中的软件中止了一个已建立的连接
:
ERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: 9f9972c50afe9e23dd8ba4a9ca1cb9d81ec950b53bcc51d75991adf601b66964 to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://127.0.0.1:5000, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=io exception Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0], cause=java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
这个错误在网上倒也能找到些东西,也是Java连接远程数据库啥的。大致就是有可能虚拟机那边响应慢,sdk发送ssl连接请求时没等响应就断开或者发了一个新的请求,把连接中断了;或者是设置了timeout,超时就直接给断开了连接;又或者是短时间内重复提交了同一个表单导致连接中断,总之,都不是我这类情况。
参考:https://blog.csdn.net/qq_54070103/article/details/127441068
https://www.bbsmax.com/A/qVdepjbr5P/
此时,每运行一次Simple.java,cmd窗口中开始不断显示channel 3: open failed: connect failed: Connection refused
:
C:\WINDOWS\system32>ssh -L 7050:localhost:7050 zcongfly@127.0.0.1 -p 2222 zcongfly@127.0.0.1's password: Last login: Fri Mar 31 08:15:03 2023 from gateway [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused [zcongfly@localhost ~]$ channel 3: open failed: connect failed: Connection refused
这一度带给过我希望,是不是只要解决了信任问题就可以连接上了。
报错4:
求助于chat-gpt:
“如果您使用SSH端口转发将CentOS虚拟机上的peer节点端口映射到本地主机上的端口,则您可以在url
中指定本地主机上的端口。在这种情况下,您应该将localhost
设置为本地主机的IP地址(通常为127.0.0.1),并将端口设置为您在SSH端口转发命令中指定的本地端口(默认为2222)。
例如,如果您在本地主机上执行了以下SSH端口转发命令:
ssh -L 2222:localhost:7051 user@192.168.1.100
则您的url
应该类似于:
"url": "grpc://127.0.0.1:2222"
这将允许Java SDK客户端通过本地主机上的端口2222连接到CentOS虚拟机上的peer节点的GRPC服务。”
如果要连接多个peer节点该如何操作呢?比如连接到peer0的7051端口、peer1的8051端口、peer2的9051端口?
如果要连接多个peer节点,您需要为每个peer节点执行SSH端口转发命令,并将它们映射到本地主机的不同端口。例如:
ssh -L 2222:localhost:7051 user@192.168.1.100 ssh -L 3333:localhost:8051 user@192.168.1.100 ssh -L 4444:localhost:9051 user@192.168.1.100
以上命令将分别将CentOS虚拟机上peer0、peer1和peer2的GRPC服务端口映射到本地端口2222、3333和4444上。
接下来,您可以在Java SDK客户端中使用以下代码来连接这些peer节点:
List<Peer> peers = new ArrayList<>(); peers.add(HFClient.createNewDefaultPeer("peer0", "grpc://127.0.0.1:2222")); peers.add(HFClient.createNewDefaultPeer("peer1", "grpc://127.0.0.1:3333")); peers.add(HFClient.createNewDefaultPeer("peer2", "grpc://127.0.0.1:4444")); Channel channel = HFClient.createNewChannel("mychannel", orderer, config); channel.addPeers(peers);
以上代码创建了一个包含所有peer节点的
peers
列表,并将其添加到一个名为mychannel
的通道中。请注意,在每个grpc://
URL中,您需要指定正确的本地端口,以便连接到相应的peer节点。这样,您就可以通过Java SDK客户端连接到多个peer节点的GRPC服务了。
先到虚拟机中在添加3333和4444的端口转发,然后以管理员身份启用3个cmd窗口,分别输入:
ssh -L 7050:localhost:7050 zcongfly@127.0.0.1 -p 2222 ssh -L 7051:localhost:7051 zcongfly@127.0.0.1 -p 3333 ssh -L 8051:localhost:8051 zcongfly@127.0.0.1 -p 4444
建立起3个ssh隧道并成功连接上之后,修改connection.json文件中各个节点url分别为:
"url": "grpcs://localhost:7050" "url": "grpcs://localhost:7051" "url": "grpcs://localhost:8051"
运行Sample.java,报错Connection closed while performing TLS negotiation
:
ERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: d2915393c441925665cb31c87a9c1803c0022707b124e35f27e72c20e762fedd to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://127.0.0.1:8051, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=Connection closed while performing TLS negotiation Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0], cause=java.nio.channels.ClosedChannelExceptionERROR org.hyperledger.fabric.sdk.Channel - Channel Channel{id: 1, name: mychannel} Sending proposal with transaction: d2915393c441925665cb31c87a9c1803c0022707b124e35f27e72c20e762fedd to Peer{ id: 2, name: peer0.department2.trace.com, channelName: mychannel, url: grpcs://127.0.0.1:8051, mspid: Department2MSP} failed because of: gRPC failure=Status{code=UNAVAILABLE, description=Connection closed while performing TLS negotiation
这回变为TSL证书错误了。。。
这个错误表明在与peer节点进行TLS协商期间,连接已经被关闭,导致无法发送建议。这通常表示由于某些网络问题或配置错误而无法建立稳定的TLS连接。
您可以尝试检查以下几点:
- 确认您的peer节点是否正确启动并正在运行。您可以使用
peer node status
命令来检查。- 检查peer节点的TLS证书和密钥是否正确生成,并且Java SDK客户端是否正确加载了这些证书和密钥。您可以检查SDK客户端的TLS设置,并确保它们与peer节点的TLS设置匹配。
- 检查您的网络环境,确保从Java SDK客户端到peer节点的网络连接是可用的,并且没有任何防火墙或网络代理阻止了通信。
如果您仍然无法解决问题,请尝试将日志级别设置为更高的水平,并检查详细的日志信息,以便更好地理解问题所在。
要检查Java SDK客户端中使用的TLS配置和证书,您可以检查以下内容:
- 客户端配置文件:检查您在创建SDK客户端时传递的配置文件。如果您使用了默认配置,该文件应为
connection-profile.yaml
。- TLS证书和密钥:检查配置文件中包含的TLS证书和密钥路径是否正确,并确保这些证书和密钥与peer节点的TLS设置匹配。
- TLS设置:检查配置文件中的TLS设置,例如是否启用了TLS,是否使用了正确的加密算法等。确保客户端的TLS设置与peer节点的TLS设置匹配。
- 日志输出:启用更详细的日志输出,以便检查TLS握手期间发生的任何错误或异常情况。您可以通过在SDK客户端代码中调用
Logger.getLogger("org.hyperledger.fabric").setLevel(Level.DEBUG)
来设置更高的日志级别。这将打印出更多的详细信息,包括TLS握手期间发生的事件和错误。- 测试工具:您可以使用OpenSSL等工具测试从SDK客户端到peer节点的TLS连接是否正常工作。例如,您可以使用以下命令来测试与peer节点的TLS连接:
openssl s_client -connect <peer-hostname>:<peer-port> -tls1_2
以上命令将使用TLS 1.2连接到指定的peer节点,并打印出与TLS握手相关的详细信息。如果该命令能够成功连接到peer节点并打印出TLS握手信息,则可以确认TLS配置和证书设置正确。
(一下午了,Java也好fabric也罢,求求你们做个人吧。。。)
TSL一下触及到知识盲区了,其实前边的ssh和什么ssl连接的Java密钥就已经是盲区了。
先记录下,让我歇会儿。