Java对接Fabric-Smart-Node提供的grpc接口

fabric-smart-node(fsn)是基于faric的,通过虚拟orderer以及peer来使整体轻量化的fabric套件。目前这套组件均基于golang开发,java想要调用目前只能通过go开发的api---fabric-smart-client。

由此,java需要直接请求fsn,需要基于fsn提供的proto文件调用。这在技术上通过grpc调用本没有难度,但是因为fabric的调用涉及复杂的证书与签名,所以此文记录一下,避免重复踩坑。

涉及本次调用的proto如下:

service.proto

syntax = "proto3";option go_package = "protos";
option cc_generic_services = true;
option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;package protos;import "commands.proto";// ViewService provides support to view management
service ViewService {// ProcessCommand processes the passed command ensuring proper access control.// The returned response allows the client to understand if the// operation was successfully executed and if not, the response// reports the reason of the failure.rpc ProcessCommand(SignedCommand) returns (SignedCommandResponse);rpc StreamCommand(SignedCommand) returns (stream SignedCommandResponse){};
}

2.command.proto

syntax = "proto3";option go_package = "protos";
option cc_generic_services = true;
option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;package protos;import "google/protobuf/timestamp.proto";
import "finality.proto";// InitiateView is used to initiate a view
message InitiateView {string fid = 1;bytes input = 2;
}message InitiateViewResponse {string cid = 1;
}// InitiateView is used to initiate a view
message CallView {string fid = 1;bytes input = 2;
}message CallViewResponse {bytes result = 1;
}message TrackView {string cid = 1;
}message TrackViewResponse {bytes payload = 1;
}// Header is a generic replay prevention and identity message to include in a signed command
message Header {// Timestamp is the local time when the message was created// by the sendergoogle.protobuf.Timestamp timestamp = 1;// Nonce is a sufficiently long random value// used to ensure the request has enough entropy.bytes nonce = 3;// Creator of the message.bytes creator = 4;// TlsCertHash represents the hash of the client's TLS certificate// when mutual TLS is enabledbytes tls_cert_hash = 5;
}// Command describes the type of operation that a client is requesting.
message Command {// Header is the header of this commandHeader header = 1;// Payload is the payload of this command. It can assume one of the following valueoneof payload {InitiateView initiateView = 2;TrackView trackView = 3;CallView callView = 4;IsTxFinal isTxFinal = 5;}
}// SignedCommand is a command that carries the signature of the command's creator.
message SignedCommand {// Command is the serialised version of a Command messagebytes command = 1;// Signature is the signature over commandbytes signature = 2;
}message CommandResponseHeader {// Timestamp is the time that the message// was created as  defined by the sendergoogle.protobuf.Timestamp timestamp = 1;// CommandHash is the hash computed on the concatenation of the SignedCommand's command and signature fields.// If not specified differently, SHA256 is used// The hash is used to link a response with its request, both for bookeeping purposes on an// asynchronous system and for security reasons (accountability, non-repudiation)bytes command_hash = 2;// Creator is the identity of the party creating this messagebytes creator = 3;
}// Error reports an application error
message Error {// Message associated with this response.string message = 1;// Payload that can be used to include metadata with this response.bytes payload = 2;
}// A CommnandResponse is returned from a server to the command submitter.
message CommandResponse {// Header of the response.CommandResponseHeader header = 1;// Payload of the response.oneof payload {Error err = 2;InitiateViewResponse initiateViewResponse = 3;TrackViewResponse trackViewResponse = 4;CallViewResponse callViewResponse = 5;IsTxFinalResponse isTxFinalResponse = 6;}
}// SignedCommandResponse is a signed command response
message SignedCommandResponse {// Response is the serialised version of a CommandResponse messagebytes response = 1;// Signature is the signature over commandbytes signature = 2;
}

3.finality.proto


syntax = "proto3";option go_package = "protos";
option cc_generic_services = true;option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;package protos;message IsTxFinal {string network = 1;string channel = 2;string txid = 3;
}message IsTxFinalResponse {bytes payload = 1;
}

根据以上proto文件可生成一下java文件

在这里我用

rpc ProcessCommand(SignedCommand) returns (SignedCommandResponse) 来举个例子

 代码如下:

package com.xxx.proto.java;import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.ByteString;
import com.google.protobuf.Timestamp;
import com.rongzer.rbaas.platform.server.fabric.sdkintegration.SampleUser;
import io.grpc.ManagedChannel;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;import org.hyperledger.fabric.sdk.identity.SigningIdentity;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.hyperledger.fabric.sdk.identity.X509SigningIdentity;
import org.hyperledger.fabric.sdk.security.CryptoSuite;import javax.net.ssl.SSLException;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.UUID;public class Test {public static void main(String[] args) throws InterruptedException, IOException, IllegalAccessException, InvocationTargetException, InvalidArgumentException, InstantiationException, NoSuchMethodException, CryptoException, ClassNotFoundException {//该文件是approver的cert.pem证书文件,需要自己在已部署的fsn服务中找到并且换掉String identityPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/msp/signcerts/approver.fsc.example.com-cert.pem";//该文件是approver的私钥文件,需要自己在已部署的fsn服务中找到并且换掉String privPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/msp/keystore/priv_sk";//该文件是approver的ca文件,需要自己在已部署的fsn服务中找到并且换掉String caPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/tls/ca.crt";File ca = new File(caPath);//File ca = new File(identityPath);String fid = "funtionName";//需要调用的方法,由fsn服务端提供JSONObject input = new JSONObject();input.put("param","paramValue");//需要调用的方法所需的参数CallView callView = CallView.newBuilder().setFid(fid).setInput(ByteString.copyFrom(input.toJSONString().getBytes(StandardCharsets.UTF_8))).build();Header header = Header.newBuilder().setTimestamp(Timestamp.getDefaultInstance()).setNonce(ByteString.copyFrom(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8))).setCreator(ByteString.readFrom(new FileInputStream(identityPath))).setTlsCertHash(com.google.protobuf.ByteString.EMPTY).build();Command command = Command.newBuilder().setCallView(callView).setHeader(header).build();StringBuilder keyStr = new StringBuilder();try (BufferedReader br = Files.newBufferedReader(Paths.get(privPath))){String line ;while ((line = br.readLine()) != null) {keyStr.append(line).append("\n");;}}catch (Exception e){throw e;}PrivateKey privateKey;String keyString = keyStr.toString();try (PEMParser parser = new PEMParser(new StringReader(keyString))) {Object key = parser.readObject();JcaPEMKeyConverter converter = new JcaPEMKeyConverter();privateKey = converter.getPrivateKey((PrivateKeyInfo) key);}catch (Exception e){throw e;}SignedCommand signedCommand = SignedCommand.newBuilder().setCommand(command.toByteString()).setSignature(ByteString.copyFrom(CryptoSuite.Factory.getCryptoSuite().sign(privateKey,command.toByteArray()))).build();String target = "ip:port";SslContextBuilder builder = GrpcSslContexts.forClient();SslContext sslContext = builder.trustManager(ca).build();ManagedChannel channel = NettyChannelBuilder.forTarget(target)//.usePlaintext().overrideAuthority("approver.fsc.example.com")//这里需要填写approver的server.crt中可使用者的DNS NAME.negotiationType(NegotiationType.TLS).sslContext(sslContext).build();ViewServiceGrpc.ViewServiceBlockingStub stub = ViewServiceGrpc.newBlockingStub(channel);SignedCommandResponse res = stub.processCommand(signedCommand);String cr = CommandResponse.parseFrom(res.getResponse()).getCallViewResponse().getResult().toStringUtf8();System.out.println(cr);}private static SslContext buildSslContext(String trustCertCollectionFilePath,String clientCertChainFilePath,String clientPrivateKeyFilePath) throws SSLException {SslContextBuilder builder = GrpcSslContexts.forClient();if (trustCertCollectionFilePath != null) {builder.trustManager(new File(trustCertCollectionFilePath));}if (clientCertChainFilePath != null && clientPrivateKeyFilePath != null) {builder.keyManager(new File(clientCertChainFilePath), new File(clientPrivateKeyFilePath));}return builder.build();}}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部