Hyperledger fabric, fabric-gateway-java sdk, 动态多组织切换, 配置和初始化 Fabric 多组织支持的指南

本文介绍如何通过 Spring Boot 配置和初始化多组织支持的合约实例

1. 修改application.yml 添加fabric多组织配置

首先,在 application.yml 文件中添加 Fabric 多组织的配置信息。这些配置信息包括合约名称、通道名称、默认客户端以及多个组织的详细信息。以下是示例配置:

fabric:
  chaincode-name: chaincodeName # 合约名称
  channel-name: mychannel
  default-client: Org1MSP
  clients:
    - mspId: Org1MSP
      peerEndpoint: 192.168.43.140:7051
      override-auth: peer0.org1.example.com
      cryptoPath: "./organizations/peerOrganizations/org1.example.com" # 从项目根目录出发的路径
      certDirPath: "users/User1@org1.example.com/msp/signcerts"
      keyDirPath: "users/User1@org1.example.com/msp/keystore"
      tlsCertPath: "peers/peer0.org1.example.com/tls/ca.crt"
    - mspId: Org2MSP
      peerEndpoint: 192.168.43.140:9051
      override-auth: peer0.org2.example.com
      cryptoPath: "./organizations/peerOrganizations/org2.example.com"
      certDirPath: "users/User1@org2.example.com/msp/signcerts"
      keyDirPath: "users/User1@org2.example.com/msp/keystore"
      tlsCertPath: "peers/peer0.org2.example.com/tls/ca.crt"

2. Fabric 多组织配置类

为了将 application.yml 中的配置属性绑定到 Java 对象上,我们需要创建一个配置类。该类使用 Spring Boot 的 @ConfigurationProperties 注解,绑定配置文件中的属性到 Java 对象中,确保类型安全和简化配置管理。

package com.trace.sys.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@ConfigurationProperties(prefix = "fabric")
@Data
public class FabricProperties {
    private String channelName;
    private String chaincodeName;
    private String defaultClient;
    private List<FabricClientConf> clients;


    @Data
    public static class FabricClientConf {
        private String mspId;
        private String peerEndpoint;
        private String overrideAuth;
        private String cryptoPath;
        private String certDirPath;
        private String keyDirPath;
        private String tlsCertPath;

    }
}

主要功能

  1. 绑定外部属性:将外部配置文件中的属性值绑定到 Java 对象的字段上。
  2. 类型安全:通过绑定配置属性到 Java Bean 上,确保配置的类型安全。
  3. 简化配置管理:使配置管理更加简洁和清晰。

3. 初始化多组织合约

在应用启动时,初始化多组织合约实例,通过使用不同的 mspId 切换组织。这部分的核心在于读取配置文件中的组织信息,并根据这些信息创建相应的合约实例。

package com.trace.sys.config;


import io.grpc.ChannelCredentials;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.TlsChannelCredentials;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.client.Contract;
import org.hyperledger.fabric.client.Gateway;
import org.hyperledger.fabric.client.Network;
import org.hyperledger.fabric.client.identity.*;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
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;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

@Slf4j
@RequiredArgsConstructor
@Configuration
public class FabricConfig {
    private static final HashMap<String, Contract> contractMap = new HashMap<>();
    private static String DEFAULT_CLIENT;

    public static Contract getDefalutContract() {
        return contractMap.get(DEFAULT_CLIENT);
    }

    public static Contract getContract(String mspId) {
        return contractMap.get(mspId);
    }

    private final FabricProperties fabricProperties;

    @PostConstruct
    public void init() throws IOException, CertificateException, InvalidKeyException {
        log.info("FabricConfig init");
        DEFAULT_CLIENT = fabricProperties.getDefaultClient();
        for (FabricProperties.FabricClientConf client : fabricProperties.getClients()) {
            log.info("FabricConfig client init : {}", client);
            Path cryptoPath = Paths.get(client.getCryptoPath());
            Path certDirPath = cryptoPath.resolve(Paths.get(client.getCertDirPath()));
            Path keyDirPath = cryptoPath.resolve(Paths.get(client.getKeyDirPath()));
            Path tlsCertPath = cryptoPath.resolve(Paths.get(client.getTlsCertPath()));
            ManagedChannel channel = newGrpcConnection(tlsCertPath, client.getPeerEndpoint(), client.getOverrideAuth());

            Gateway.Builder builder = Gateway.newInstance()
                    .identity(newIdentity(certDirPath, client.getMspId()))
                    .signer(newSigner(keyDirPath))
                    .connection(channel)
                    // Default timeouts for different gRPC calls
                    .evaluateOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
                    .endorseOptions(options -> options.withDeadlineAfter(15, TimeUnit.SECONDS))
                    .submitOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
                    .commitStatusOptions(options -> options.withDeadlineAfter(1, TimeUnit.MINUTES));

            Gateway gateway = builder.connect();
            // Get a network instance representing the channel where the smart contract is
            // deployed.
            Network network = gateway.getNetwork(fabricProperties.getChannelName());
            // Get the smart contract from the network.
            Contract contract = network.getContract(fabricProperties.getChaincodeName());
            contractMap.put(client.getMspId(), contract);
        }
        log.info("FabricConfig init success");
    }

    private static ManagedChannel newGrpcConnection(Path tlsCertPath, String peerEndpoint, String overrideAuth) throws IOException {
        ChannelCredentials build = TlsChannelCredentials.newBuilder()
                .trustManager(tlsCertPath.toFile())
                .build();
        return Grpc.newChannelBuilder(peerEndpoint, build)
                .overrideAuthority(overrideAuth)
                .build();
    }

    private static Identity newIdentity(Path certDirPath, String mspId) throws IOException, CertificateException {

        try (BufferedReader bufferedReader = Files.newBufferedReader(getFirstFilePath(certDirPath))) {
            X509Certificate certificate = Identities.readX509Certificate(bufferedReader);
            return new X509Identity(mspId, certificate);
        }
    }

    private static Signer newSigner(Path keyDirPath) throws IOException, InvalidKeyException {
        try (BufferedReader keyReader = Files.newBufferedReader(getFirstFilePath(keyDirPath))) {
            PrivateKey privateKey = Identities.readPrivateKey(keyReader);
            return Signers.newPrivateKeySigner(privateKey);
        }
    }

    private static Path getFirstFilePath(Path dirPath) throws IOException {
        try (Stream<Path> keyFiles = Files.list(dirPath)) {
            return keyFiles.findFirst()
                    .orElseThrow(() -> new IOException("No files found in " + dirPath));
        }
    }
}

4. 使用示例

在使用时,通过 mspId 获取相应的合约实例。如果 mspId·为空,则返回默认的合约实例。

	public static Contract getContract(String mspId) {
		if (mspId == null || mspId.isEmpty()){
			return FabricConfig.getDefalutContract();
		}
		return FabricConfig.getContract(mspId);
 	}

结论

通过本文的步骤,您可以在 Spring Boot 应用中配置和初始化 Hyperledger Fabric 的多组织支持,实现多组织间的合约交互。这样不仅提高了配置的灵活性和类型安全性,还简化了配置管理,使得在实际项目中更加容易维护和扩展。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值