在进行fabric-sdk-java 2.2.0的学习中,在进行创建channel和添加peer的过程,网上能查到的资料很少并且还不全面,在这边我根据官方给的测试案例和自己的研究,成功的完成了这个过程,下面进行一些总结和代码的展示,

参考案例地址:https://github.com/hyperledger/fabric-sdk-java/blob/release-2.2/src/test/java/org/hyperledger/fabric/sdkintegration/End2endLifecycleIT.java

基于 fabric2.2.0 的测试案例进行测试 test-network

项目目录

src
 -- main
    -- java
       --com
         --ictnj
           -- java
              -- service     -- 里面存放的是核心代码
    -- sources
       -- crypto-config
         -- peerOrganizations
         -- OrdererOrganizations
       -- channel1.tx
       -- application.yaml

POM文件 (在这里我使用的是springboot框架,使用的是nacos和dubbo进行的服务注册和服务发现,如果不使用的话,可以将相关依赖删除,也不影响项目的正常操作)

    <properties>
        <java.version>1.8</java.version>
        <dubbo.version>2.7.8</dubbo.version>
        <mybatis-plus>3.4.2</mybatis-plus>
        <mysql>8.0.17</mysql>
        <fabric.version>2.0.0</fabric.version>
        <cloud-nacos>2.2.1.RELEASE</cloud-nacos>
    </properties>
​
<dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>application_apis</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hyperledger.fabric</groupId>
            <artifactId>fabric-gateway-java</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
​
​
        <!-- https://mvnrepository.com/artifact/org.hyperledger.fabric-chaincode-java/fabric-chaincode-shim -->
        <dependency>
            <groupId>org.hyperledger.fabric-chaincode-java</groupId>
            <artifactId>fabric-chaincode-shim</artifactId>
            <version>2.2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.everit-org.json-schema</groupId>
                    <artifactId>org.everit.json.schema</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.owlike/genson -->
        <dependency>
            <groupId>com.owlike</groupId>
            <artifactId>genson</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>

在source下的crypto-config 下面的文件为启动fabric生成的文件,可以直接进行替换

我们还需要一个 .tx文件,用来生成通道,

获取文件步骤:

1.在test-network的目录下,进行工具脚本路径的添加  (默认已经启动了fabric服务)

export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${PWD}/configtx
​
2.创建创世块

configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block
3.生成.tx文件

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel1.tx -channelID channel1
这里的名称可以自己进行定义,我这里定义的是 channel1.tx  

//  创建用户注册类,在这里 必须实现 User接口,

public class UserContextImp implements User, Serializable {  
    private static final long serialVersionUID = 11384281131231L;
    protected String name;        
    protected Set<String> roles;
    protected String account;
    protected String affiliation;
    protected Enrollment enrollment;
    protected String mspId;
​
    public void setName(String name) {
        this.name = name;
    }
​
    public void setRoles(Set<String> roles) {
        this.roles = roles;
    }
​
    public void setAccount(String account) {
        this.account = account;
    }
​
    public void setAffiliation(String affiliation) {
        this.affiliation = affiliation;
    }
​
    public void setEnrollment(Enrollment enrollment) {
        this.enrollment = enrollment;
    }
​
    public void setMspId(String mspId) {
        this.mspId = mspId;
    }
​
    @Override
    public String getName() {
        return name;
    }
​
    @Override
    public Set<String> getRoles() {
        return roles;
    }
​
    @Override
    public String getAccount() {
        return account;
    }
​
    @Override
    public String getAffiliation() {
        return affiliation;
    }
​
    @Override
    public Enrollment getEnrollment() {
        return enrollment;
    }
​
    @Override
    public String getMspId() {
        return mspId;
    }
}

然后进行HFClient对象的生成

public class FabricClient {
​
    private HFClient instance;
​
    /**
     * Return an instance of HFClient.
     *
     * @return
     */
    public HFClient getInstance() {
        return instance;
    }
​
    /**
     * Constructor
     *
     * @param context
     * @throws CryptoException
     * @throws InvalidArgumentException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public FabricClient(User context) throws CryptoException, InvalidArgumentException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
        CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
        // setup the client
        instance = HFClient.createNewInstance();
        instance.setCryptoSuite(cryptoSuite);
        instance.setUserContext(context);
    }

一些基本配置类

package com.ictnj.java.admin.config;
​
public class TestConfig {
// 因为我这边用的是微服务,所有,路径必须要包括我的模块名称
//    orderer
    public static final String ORDERER_PEMFILE="application_java_admin/src/main/resources/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt";
​
    public static final String ORDERER_CERT="application_java_admin/src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/tls/client.crt";
​
    public static final String ORDERER_KEY="application_java_admin/src/main/resources/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/tls/client.key";
​
    public static final String ORDER_NAME="orderer.example.com";
//    peer0.org1
​
    public static final String PEER1_PEMFILE="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt";
​
    public static final String PEER1_CERT="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/client.crt";
​
    public static final String PEER1_KEY="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/client.key";
​
    public static final String PEER1_NAME="peer0.org1.example.com";
//    peer0.org2
​
    public static final String PEER2_PEMFILE="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt";
​
    public static final String PEER2_CERT="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/tls/client.crt";
​
    public static final String PEER2_KEY="application_java_admin/src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/tls/client.key";
​
    public static final String PEER2_NAME="peer0.org2.example.com";
​
}
​
​

然后下面进行基本业务的处理过程

public class CreateChannel {
​
    public void createChannel() {
        try {
            // 创建org1的用户管理员对象的创建
            UserContextImp org1Admin = new UserContextImp();
            // 秘钥地址
            String pkPath = "application_java_admin/src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore";
            File pkFile1 = new File(pkPath);
            File[] pkFiles1 = pkFile1.listFiles();
            // 证书地址
            String certPath = "application_java_admin/src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts";
            File certFile1 = new File(certPath);
            File[] certFiles1 = certFile1.listFiles();
            // 进行证书和秘钥的登记
            Enrollment enrollment = getEnrollment(pkPath, pkFiles1[0].getName(), certPath, certFiles1[0].getName());
            org1Admin.setEnrollment(enrollment);
            org1Admin.setName("admin");
            org1Admin.setMspId("Org1MSP");
            org1Admin.setAffiliation("org1");
​
            // org2的用户管理对象的创建
            UserContextImp org2Admin = new UserContextImp();
            String pkPath2 = "application_java_admin/src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore";
            File pkFile2 = new File(pkPath2);
            File[] pkFiles2 = pkFile2.listFiles();
            String certPath2 = "application_java_admin/src/main/resources/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts";
            File certFile2 = new File(certPath2);
            File[] certFiles2 = certFile2.listFiles();
            Enrollment enrollment1 = getEnrollment(pkPath2, pkFiles2[0].getName(), certPath2, certFiles2[0].getName());
            org2Admin.setEnrollment(enrollment1);
            org2Admin.setName("user2");
            org2Admin.setMspId("Org2MSP");
            org2Admin.setAffiliation("org2");
​
            FabricClient fabricClient = new FabricClient(org1Admin);
//            Properties orderPro = new Properties();
            // properties配置的信息导入  
            Properties orderPro = loadTLSFile(TestConfig.ORDERER_PEMFILE, TestConfig.ORDERER_CERT, TestConfig.ORDERER_KEY, TestConfig.ORDER_NAME, "OrdererMSP");
            // 通过HFClient对象新建一个orderer对象,
            Orderer orderer = fabricClient.getInstance().newOrderer("orderer.example.com", "grpcs://orderer.example.com:7050", orderPro);
             // 创建通道配置对象,参数为创建的.tx通道文件
            ChannelConfiguration channelConfiguration = new ChannelConfiguration(new File("application_java_admin/src/main/resources/channel1.tx"));
​
            byte[] channelConfigurationSignatures = fabricClient.getInstance().getChannelConfigurationSignature(channelConfiguration, org1Admin);
            // 新建一个channel
            Channel channel1 = fabricClient.getInstance().newChannel("channel1", orderer, channelConfiguration, channelConfigurationSignatures);
//            Channel channel1 = fabricClient.getInstance().newChannel("mychannel");
​
            for (Peer peer : channel1.getPeers()) {
                System.out.println(peer.getName());
            }
            // 加载配置
            Properties peer1Pro = loadTLSFile(TestConfig.PEER1_PEMFILE, TestConfig.PEER1_CERT, TestConfig.PEER1_KEY, TestConfig.PEER1_NAME, "Org1MSP");
            //新建peer1
            Peer peer0_Org1 = fabricClient.getInstance().newPeer(TestConfig.PEER1_NAME, "grpcs://192.168.127.130:7051", peer1Pro);
            
            // 新建peer2
            Properties peer2Pro = loadTLSFile(TestConfig.PEER2_PEMFILE, TestConfig.PEER2_CERT, TestConfig.PEER2_KEY, TestConfig.PEER2_NAME, "Org2MSP");
            Peer peer0_Org2 = fabricClient.getInstance().newPeer(TestConfig.PEER2_NAME, "grpcs://192.168.127.130:9051", peer2Pro);
            
            // 将peer加入到channel中 
            channel1.joinPeer(peer0_Org1, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(PeerRole.ENDORSING_PEER, PeerRole.LEDGER_QUERY, PeerRole.CHAINCODE_QUERY, PeerRole.EVENT_SOURCE)));
            channel1.addOrderer(orderer);
            // 进行判断,是否加入成功
            assertFalse(channel1.getPeers(EnumSet.of(Peer.PeerRole.EVENT_SOURCE)).isEmpty());
            assertFalse(channel1.getPeers(PeerRole.NO_EVENT_SOURCE).isEmpty());
​
           // 进行初始化通道
            channel1.initialize();
​
​
            Logger.getLogger(CreateChannel.class.getName()).log(Level.INFO, "Channel created " + channel1.getName());
//                   切换为org2的用户进行上述操作,添加peer
            fabricClient.getInstance().setUserContext(org2Admin);
            channel1 = fabricClient.getInstance().getChannel("channel1");
            channel1.joinPeer(peer0_Org2, Channel.PeerOptions.createPeerOptions().setPeerRoles(EnumSet.of(Peer.PeerRole.ENDORSING_PEER, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.CHAINCODE_QUERY, Peer.PeerRole.EVENT_SOURCE)));
            Iterator<Peer> iterator = channel1.getPeers().iterator();
​
            assertFalse(channel1.getPeers(EnumSet.of(Peer.PeerRole.EVENT_SOURCE)).isEmpty());
            assertFalse(channel1.getPeers(PeerRole.NO_EVENT_SOURCE).isEmpty());
            channel1.initialize();
       } catch (Exception e) {
            e.printStackTrace();
        }
    }
              //    将配置项导入到配置pro中
    private static Properties loadTLSFile(String servicePath, String certPath, String keyPath, String hostName, String Msp) throws IOException {
        Properties properties = new Properties();
//    # 其实只需要一个TLS根证书就可以了,比如TLS相关的秘钥等都是可选的
        properties.put("pemBytes", Files.readAllBytes(Paths.get(servicePath)));
        properties.setProperty("clientCertFile", certPath);
        properties.setProperty("clientKeyFile", keyPath);
        properties.setProperty("sslProvider", "openSSL");
        properties.setProperty("negotiationType", "TLS");
        properties.setProperty("trustServerCertificate", "true");
        properties.setProperty("hostnameOverride", hostName);
        properties.setProperty("ssl-target-name-override", hostName);
        if (hostName.contains("peer")) {
            properties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);
//            properties.setProperty("org.hyperledger.fabric.sdk.peer.organization_mspid", Msp);
​
        }
        if (hostName.contains("orderer")) {
            properties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[]{5L, TimeUnit.MINUTES});
            properties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[]{8L, TimeUnit.SECONDS});
            properties.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[]{true});
//            properties.setProperty("org.hyperledger.fabric.sdk.orderer.organization_mspid", Msp);
//
        }
        return properties;
    }
​
    // 证书和秘钥的登记过程
    public static CAEnrollment getEnrollment(String keyFolderPath, String keyFileName, String certFolderPath, String certFileName)
            throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CryptoException {
        PrivateKey key = null;
        String certificate = null;
        InputStream isKey = null;
        BufferedReader brKey = null;
​
        try {
​
            isKey = new FileInputStream(keyFolderPath + File.separator + keyFileName);
            brKey = new BufferedReader(new InputStreamReader(isKey));
            StringBuilder keyBuilder = new StringBuilder();
​
            for (String line = brKey.readLine(); line != null; line = brKey.readLine()) {
                if (line.indexOf("PRIVATE") == -1) {
                    keyBuilder.append(line);
                }
            }
​
            certificate = new String(Files.readAllBytes(Paths.get(certFolderPath, certFileName)));
​
            byte[] encoded = DatatypeConverter.parseBase64Binary(keyBuilder.toString());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
            KeyFactory kf = KeyFactory.getInstance("EC");
            key = kf.generatePrivate(keySpec);
        } finally {
            isKey.close();
            brKey.close();
        }
​
        CAEnrollment enrollment = new CAEnrollment(key, certificate);
        return enrollment;
    }
​

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐