尝试将 hyperledger fabric SDK 与 spring REST 控制器集成时出现以下错误

Getting the following error when trying to integrate hyperledger fabric SDK with spring REST controller

我正在做的事情背后的想法是使用 Spring REST API 公开 hyperledger fabric 区块链。 当我构建我的代码(withourt REST)并使用 java 命令调用它时,它工作正常并且 returns 来自 Hyperledger fabric 的数据。 但是,当我尝试在 REST 控制器中实现它时,我发现自己被阻止了。

下面是我的 Rest Controller 代码:

@RestController
public class RestWebController {

private static final Logger log = Logger.getLogger(RestWebController.class);

@RequestMapping(value = "/getall", method = RequestMethod.GET)
public void getAll(){
    try {
       HFCAClient caClient = getHfCaClient("http://localhost:7054", null);

    // enroll or load admin
    AppUser admin = getAdmin(caClient);
    log.info(admin);

    // register and enroll new user
    AppUser appUser = getUser(caClient, admin, "hfuser");
    log.info(appUser);

    // get HFC client instance
    HFClient client = getHfClient();
    // set user context
    client.setUserContext(admin);

    // get HFC channel using the client
    Channel channel = getChannel(client);
    log.info("Channel: " + channel.getName());

    // call query blockchain example
    queryBlockChain(client);
}
catch (Exception e) {
        e.printStackTrace();

    }
}



    /**
 * Invoke blockchain query
 *
 * @param client The HF Client
 * @throws ProposalException
 * @throws InvalidArgumentException
 */
static void queryBlockChain(HFClient client) throws ProposalException, InvalidArgumentException {
    // get channel instance from client
    Channel channel = client.getChannel("mychannel");
    // create chaincode request
    QueryByChaincodeRequest qpr = client.newQueryProposalRequest();
    // build cc id providing the chaincode name. Version is omitted here.
    ChaincodeID fabcarCCId = ChaincodeID.newBuilder().setName("poc-app").build();
    qpr.setChaincodeID(fabcarCCId);
    // CC function to be called
    qpr.setFcn("queryAllTransactions");
    Collection<ProposalResponse> res = channel.queryByChaincode(qpr);
    // display response
    for (ProposalResponse pres : res) {
        String stringResponse = new String(pres.getChaincodeActionResponsePayload());
        log.info(stringResponse);
    }
}

/**
 * Initialize and get HF channel
 *
 * @param client The HFC client
 * @return Initialized channel
 * @throws InvalidArgumentException
 * @throws TransactionException
 */
static Channel getChannel(HFClient client) throws InvalidArgumentException, TransactionException {
    // initialize channel
    // peer name and endpoint in fabcar network
    Peer peer = client.newPeer("peer0.org1.example.com", "grpc://localhost:7051");
    // eventhub name and endpoint in fabcar network
    EventHub eventHub = client.newEventHub("eventhub01", "grpc://localhost:7053");
    // orderer name and endpoint in fabcar network
    Orderer orderer = client.newOrderer("orderer.example.com", "grpc://localhost:7050");
    // channel name in fabcar network
    Channel channel = client.newChannel("mychannel");
    channel.addPeer(peer);
    channel.addEventHub(eventHub);
    channel.addOrderer(orderer);
    channel.initialize();
    return channel;
}

/**
 * Create new HLF client
 *
 * @return new HLF client instance. Never null.
 * @throws CryptoException
 * @throws InvalidArgumentException
 */
static HFClient getHfClient() throws Exception {
    // initialize default cryptosuite
    CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
    // setup the client
    HFClient client = HFClient.createNewInstance();
    client.setCryptoSuite(cryptoSuite);
    return client;
}


/**
 * Register and enroll user with userId.
 * If AppUser object with the name already exist on fs it will be loaded and
 * registration and enrollment will be skipped.
 *
 * @param caClient  The fabric-ca client.
 * @param registrar The registrar to be used.
 * @param userId    The user id.
 * @return AppUser instance with userId, affiliation,mspId and enrollment set.
 * @throws Exception
 */
static AppUser getUser(HFCAClient caClient, AppUser registrar, String userId) throws Exception {
    AppUser appUser = tryDeserialize(userId);
    if (appUser == null) {
        RegistrationRequest rr = new RegistrationRequest(userId, "org1");
        String enrollmentSecret = caClient.register(rr, registrar);
        Enrollment enrollment = caClient.enroll(userId, enrollmentSecret);
        appUser = new AppUser(userId, "org1", "Org1MSP", enrollment);
        serialize(appUser);
    }
    return appUser;
}

/**
 * Enroll admin into fabric-ca using {@code admin/adminpw} credentials.
 * If AppUser object already exist serialized on fs it will be loaded and
 * new enrollment will not be executed.
 *
 * @param caClient The fabric-ca client
 * @return AppUser instance with userid, affiliation, mspId and enrollment set
 * @throws Exception
 */
static AppUser getAdmin(HFCAClient caClient) throws Exception {
    AppUser admin = tryDeserialize("admin");
    if (admin == null) {
        Enrollment adminEnrollment = caClient.enroll("admin", "adminpw");
        admin = new AppUser("admin", "org1", "Org1MSP", adminEnrollment);
        serialize(admin);
    }
    return admin;
}

/**
 * Get new fabic-ca client
 *
 * @param caUrl              The fabric-ca-server endpoint url
 * @param caClientProperties The fabri-ca client properties. Can be null.
 * @return new client instance. never null.
 * @throws Exception
 */
static HFCAClient getHfCaClient(String caUrl, Properties caClientProperties) throws Exception {
    CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
    HFCAClient caClient = HFCAClient.createNewInstance(caUrl, caClientProperties);
    caClient.setCryptoSuite(cryptoSuite);
    return caClient;
}


// user serialization and deserialization utility functions
// files are stored in the base directory

/**
 * Serialize AppUser object to file
 *
 * @param appUser The object to be serialized
 * @throws IOException
 */
static void serialize(AppUser appUser) throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(
            Paths.get(appUser.getName() + ".jso")))) {
        oos.writeObject(appUser);
    }
}

/**
 * Deserialize AppUser object from file
 *
 * @param name The name of the user. Used to build file name ${name}.jso
 * @return
 * @throws Exception
 */
static AppUser tryDeserialize(String name) throws Exception {
    if (Files.exists(Paths.get(name + ".jso"))) {
        return deserialize(name);
    }
    return null;
}

static AppUser deserialize(String name) throws Exception {
    try (ObjectInputStream decoder = new ObjectInputStream(
            Files.newInputStream(Paths.get(name + ".jso")))) {
        return (AppUser) decoder.readObject();
    }
}

I 运行 spring boot using mvn spring-boot:运行,服务器正常启动。这是我的 pom.xml :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>


    <dependency>
        <groupId>org.hyperledger.fabric-sdk-java</groupId>
        <artifactId>fabric-sdk-java</artifactId>
        <version>1.0.0</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

在 localhost:8080/getall 上调用服务器后,出现此错误:

2018-07-13 14:56:33.439  WARN 24669 --- [ault-executor-0] io.grpc.internal.ChannelExecutor         : Runnable threw exception in ChannelExecutor

java.lang.NoClassDefFoundError: io/netty/handler/codec/http2/internal/hpack/Decoder
    at io.grpc.netty.GrpcHttp2HeadersDecoder.<init>(GrpcHttp2HeadersDecoder.java:85) ~[grpc-netty-1.3.0.jar:1.3.0]
    at io.grpc.netty.GrpcHttp2HeadersDecoder$GrpcHttp2ClientHeadersDecoder.<init>(GrpcHttp2HeadersDecoder.java:147) ~[grpc-netty-1.3.0.jar:1.3.0]
    at io.grpc.netty.NettyClientHandler.newHandler(NettyClientHandler.java:119) ~[grpc-netty-1.3.0.jar:1.3.0]
    at io.grpc.netty.NettyClientTransport.start(NettyClientTransport.java:190) ~[grpc-netty-1.3.0.jar:1.3.0]
    at io.grpc.internal.ForwardingConnectionClientTransport.start(ForwardingConnectionClientTransport.java:44) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.InternalSubchannel.startNewTransport(InternalSubchannel.java:216) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.InternalSubchannel.obtainActiveTransport(InternalSubchannel.java:186) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.ManagedChannelImpl$SubchannelImplImpl.obtainActiveTransport(ManagedChannelImpl.java:812) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.GrpcUtil.getTransportFromPickResult(GrpcUtil.java:592) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.DelayedClientTransport.reprocess(DelayedClientTransport.java:295) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.ManagedChannelImpl$LbHelperImpl.run(ManagedChannelImpl.java:718) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.ChannelExecutor.drain(ChannelExecutor.java:87) ~[grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.ManagedChannelImpl$LbHelperImpl.runSerialized(ManagedChannelImpl.java:709) [grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.ManagedChannelImpl$NameResolverListenerImpl.onAddresses(ManagedChannelImpl.java:758) [grpc-core-1.3.0.jar:1.3.0]
    at io.grpc.internal.DnsNameResolver.run(DnsNameResolver.java:175) [grpc-core-1.3.0.jar:1.3.0]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_171]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_171]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_171]
Caused by: java.lang.ClassNotFoundException: io.netty.handler.codec.http2.internal.hpack.Decoder
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_171]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_171]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_171]
    ... 18 common frames omitted

2018-07-13 14:56:35.321 ERROR 24669 --- [nio-8080-exec-2] o.hyperledger.fabric.sdk.OrdererClient   : sendDeliver time exceeded for orderer

org.hyperledger.fabric.sdk.exception.TransactionException: sendDeliver time exceeded for orderer
    at org.hyperledger.fabric.sdk.OrdererClient.sendDeliver(OrdererClient.java:274) ~[fabric-sdk-java-1.0.0.jar:na]
    at org.hyperledger.fabric.sdk.Orderer.sendDeliver(Orderer.java:165) [fabric-sdk-java-1.0.0.jar:na]
    at org.hyperledger.fabric.sdk.Channel.getLatestBlock(Channel.java:1074) [fabric-sdk-java-1.0.0.jar:na]
    at org.hyperledger.fabric.sdk.Channel.getConfigurationBlock(Channel.java:898) [fabric-sdk-java-1.0.0.jar:na]
    at org.hyperledger.fabric.sdk.Channel.parseConfigBlock(Channel.java:826) [fabric-sdk-java-1.0.0.jar:na]
    at org.hyperledger.fabric.sdk.Channel.initialize(Channel.java:526) [fabric-sdk-java-1.0.0.jar:na]
    at com.example.demo.controller.RestWebController.getChannel(RestWebController.java:144) [classes/:na]
    at com.example.demo.controller.RestWebController.getAll(RestWebController.java:84) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) [spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) [spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) [spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) [tomcat-embed-core-8.5.31.jar:8.5.31]

注意:通过独立调用 .jar,甚至 运行 连接 hyperledger fabric nodeJS sdk,网络工作正常。

NoClassDefFoundError 表示版本冲突。 gRPC 被编译为使用运行时不存在的方法。

使用 mvn dependency:tree 我看到正在使用 Netty 版本 4.1.25.Final。但是看着 fabric-sdk-java 1.0.0, it is using grpc-netty 1.3.0 and Netty 4.1.8.Final. spring-boot-starter-parent is pulling in spring-boot-dependencies 选择 Netty 4.1.25.Final.

我建议升级到 fabric-sdk-java 1.1.0,它使用 grpc-netty 1.11.0 和 Netty 4.1.23.Final。然后覆盖 spring-boot-dependencies 以使用 Netty 4.1.23.Final 通过设置 netty.version 属性:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <netty.version>4.1.23.Final</netty.version> <!-- add this line -->
</properties>

我也遇到了这个问题,感谢Eric Anderson的解答,我解决了问题

完全正确NoClassDefFoundError是版本冲突的意思。

我的版本:fabric-sdk-java 1.0.0 , Spring Boot 2.2.6.RELEASE.

fabric-sdk-java1.0.0 的 netty 版本是 4.1.8.Final,但是 SpringBoot 2.2.6.RELEASEparent 的 netty 版本是 4.1 .48.Final,如果什么都不做,fabric-sdk-java可以用4.1.48.Final,所以报错。

这是我的解决方案:

<properties>
    <java.version>1.8</java.version>
    <netty.version>4.1.8.Final</netty.version>
</properties>