作为学习的记录。我是在虚拟机上搭建了fabric网络和通道,并部署了官方示例fabcar链码,现在想在windows上通过fabric-gateway-java 实现对fabric上链码的调用。


fabric的版本是2.4.0,编译器是IDEA,代码基于springboot框架。

fanric环境的搭建、test-network及通道的创建、链码的部署这里不再赘述。


首先导入如下依赖:

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

        <dependency>
            <groupId>org.hyperledger.fabric</groupId>
            <artifactId>fabric-gateway-java</artifactId>
            <version>2.2.0</version>
        </dependency>

代码的结构,我的结构是这样的:

 首先,我们需要将test-network建立好之后,文件夹test-neteork/organizations下的orderorganizations和peerorganizations文件从虚拟机中复制到代码中,我是放在了resources/crypto-config目录下。

随后写的是链接建立文件,connection.json。

{
  "name": "basic-network",
  "version": "1.0.0",
  "dependencies": {
  },
  "client": {
    "organization": "Org1",
    "connection": {
      "timeout": {
        "peer": {
          "endorser": "300"
        },
        "orderer": "300"
      }
    }
  },
  "channels": {
    "mychannel": {
      "orderers": [
        "orderer.example.com"
      ],
      "peers": {
        "peer0.org1.example.com": {
          "endorsingPeer": true,
          "chaincodeQuery": true,
          "ledgerQuery": true,
          "eventSource": true
        },
        "peer0.org2.example.com": {
          "endorsingPeer": true,
          "chaincodeQuery": true,
          "ledgerQuery": true,
          "eventSource": true
        }
      }
    }
  },
  "organizations": {
    "Org1": {
      "mspid": "Org1MSP",
      "peers": [
        "peer0.org1.example.com"
      ],
      "certificateAuthorities": [
        "ca-org1"
      ],
      "adminPrivateKeyPEM": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk"
      },
      "signedCertPEM": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem"
      }
    },
    "Org2": {
      "mspid": "Org2MSP",
      "peers": [
        "peer0.org2.example.com"
      ],
      "certificateAuthorities": [
        "ca-org2"
      ],
      "adminPrivateKeyPEM": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/priv_sk"
      },
      "signedCertPEM": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem"
      }
    }
  },
  "orderers": {
    "orderer.example.com": {
      "url": "grpcs://192.168.16.129:7050",
      "mspid": "OrdererMSP",
      "grpcOptions": {
        "ssl-target-name-override": "orderer.example.com",
        "hostnameOverride": "orderer.example.com"
      },
      "tlsCACerts": {
        "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt"
      },
      "adminPrivateKeyPEM": {
        "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/priv_sk"
      },
      "signedCertPEM": {
        "path": "src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem"
      }
    }
  },
  "peers": {
    "peer0.org1.example.com": {
      "url": "grpcs://192.168.16.129:7051",
      "grpcOptions": {
        "ssl-target-name-override": "peer0.org1.example.com",
        "hostnameOverride": "peer0.org1.example.com",
        "request-timeout": 120001
      },
      "tlsCACerts": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt"
      }
    },
    "peer0.org2.example.com": {
      "url": "grpcs://192.168.16.129:9051",
      "grpcOptions": {
        "ssl-target-name-override": "peer0.org2.example.com",
        "hostnameOverride": "peer0.org2.example.com",
        "request-timeout": 120001
      },
      "tlsCACerts": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
      }
    }
  },
  "certificateAuthorities": {
    "ca-org1": {
      "url": "https://192.168.16.129:7054",
      "grpcOptions": {
        "verify": true
      },
      "tlsCACerts": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"
      },
      "registrar": [
        {
          "enrollId": "admin",
          "enrollSecret": "adminpw"
        }
      ]
    },
    "ca-org2": {
      "url": "https://192.168.16.129:8054",
      "grpcOptions": {
        "verify": true
      },
      "tlsCACerts": {
        "path": "src/main/resources/crypto-config/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem"
      },
      "registrar": [
        {
          "enrollId": "admin",
          "enrollSecret": "adminpw"
        }
      ]
    }
  }
}

其中的ip换成自己虚拟机的ip,文件路径根据自己存放的orderorganizations和peerorganizations的目录进行调整。

之后配置properties文件:

fabric.networkConnectionConfigPath = src/main/resources/connection.json

fabric.certificatePath = src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem

fabric.privateKeyPath = src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/priv_sk

指定了,connection,json文件的位置,和后续链接用到的私钥和证书目录。

之后就可以开始写代码了hh。

首先是配置类的代码,主要功能是从properties文件中读取需要的参数值

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "fabric")
@Data

public class HyperLedgerFabricProperties {


    String networkConnectionConfigPath;
    String certificatePath;
    String privateKeyPath;


}

之后还是一个配置类,用来获取gateway.

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Identities;
import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallets;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Configuration
@AllArgsConstructor
@Slf4j
public class HyperLedgerConfig {

        final HyperLedgerFabricProperties hyperLedgerFabricProperties;

        @Bean
        public Gateway gateway() throws Exception {


            BufferedReader certificateReader = Files.newBufferedReader(Paths.get(hyperLedgerFabricProperties.getCertificatePath()), StandardCharsets.UTF_8);

            X509Certificate certificate = Identities.readX509Certificate(certificateReader);

            BufferedReader privateKeyReader = Files.newBufferedReader(Paths.get(hyperLedgerFabricProperties.getPrivateKeyPath()), StandardCharsets.UTF_8);

            PrivateKey privateKey = Identities.readPrivateKey(privateKeyReader);

            Wallet wallet = Wallets.newInMemoryWallet();
            wallet.put("user1" , Identities.newX509Identity("Org1MSP", certificate , privateKey));


            Gateway.Builder builder = Gateway.createBuilder()
               .identity(wallet , "user1")
               .networkConfig(Paths.get("src/main/resources/connection.json"));

            Gateway gateway = builder.connect();


            log.info("=========================================== connected fabric gateway {} " , gateway);

            return gateway;
        }
}

没有问题以后,就是controller的代码,这里就以queryAllCars命令为例。

import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.StringUtils;
import org.hyperledger.fabric.gateway.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeoutException;


@RestController
@RequestMapping("/car")
@Slf4j
@AllArgsConstructor
public class Fabriccontroller {
    final Gateway gateway;



    @GetMapping("/queryall")
    public Map<String, Object> queryall() throws ContractException {

        Map<String, Object> result = Maps.newConcurrentMap();
        Contract contract = getContract();
        byte[] car = contract.evaluateTransaction("queryAllCars");

        result.put("result", StringUtils.newStringUtf8(car));
      

        return result;
    }

    private Network getNetwork(){
        return gateway.getNetwork("mychannel");
    }

    private  Contract getContract(){
        return getNetwork().getContract("fabcar");
    }


}

最后getnetwork()和getcontract()的参数分别是通道的名称和链码的名称,根据自己的情况做调整。

最后,运行主启动类。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties
public class FabricJdkApplication {

    public static void main(String[] args) {
        SpringApplication.run(FabricJdkApplication.class, args);
    }

}

注意:一定要在linux中先运行起来测试网络并安装好链码后才能够连接成功。

浏览器测试一下,返回查询数据

还有一点注意的是,当我们这一次工作结束,关闭了区块链网络。下一次在重启网络,要更新代码中的 orderorganizations和peerorganizations,替换成新网络的属性文件,否则自然是链接不上的。

学习中参考了B站“老哥没有饿意”的视频。作为初学者,如有问题和错误,欢迎大家的讨论和指正。

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐