超级账本Java SDK工作示例

14

我目前正在深入研究Hyperledger Fabric,但是我无法使用Java SDK(指的是1.0.0-beta版本)来启动。是否有一个可行的示例,从连接到Fabric节点开始,进行查询等操作?到目前为止,通过广泛的谷歌搜索,我找到的都是“让我们编写一些链代码”的示例。

3个回答

15

这是一个示例,实现了fabcar(query.js和invoke.js——仅查询一辆汽车并更改所有者)的部分功能。

我在Windows上使用了Java8。如果您使用其他操作系统,请相应更新路径。

为避免使用额外的库(需要处理证书),我没有使用任何 json 实现。

您需要运行 fabcar 示例。 而且(因为 'no json'):

  1. 将私钥(来自示例的 cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv)放入 c:\tmp\cert\PeerAdm.priv
  2. 将来自 PeerAdmin 文件的证书(json 的“certificate”值,换行符替换为新行符)放入 c:\tmp\cert\PeerAdm.cert

代码(fabrictest/fabcar/Program.java):

package fabrictest.fabcar;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import javax.xml.bind.DatatypeConverter;

import org.hyperledger.fabric.sdk.ChaincodeID;
import org.hyperledger.fabric.sdk.Channel;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.HFClient;
import org.hyperledger.fabric.sdk.ProposalResponse;
import org.hyperledger.fabric.sdk.QueryByChaincodeRequest;
import org.hyperledger.fabric.sdk.TransactionProposalRequest;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoSuite;

public class Program {

    private static HFClient client = null;

    public static void main(String[] args) throws Throwable {
        /*
         * wallet_path: path.join(__dirname, './creds'), user_id: 'PeerAdmin',
         * channel_id: 'mychannel', chaincode_id: 'fabcar', network_url:
         * 'grpc://192.168.99.100:7051', orderer: grpc://192.168.99.100:7050
         * 
         */

        // just new objects, without any payload inside
        client = HFClient.createNewInstance();
        CryptoSuite cs = CryptoSuite.Factory.getCryptoSuite();
        client.setCryptoSuite(cs);

        // We implement User interface below in code
        // folder c:\tmp\creds should contain PeerAdmin.cert (extracted from HF's fabcar
        // example's PeerAdmin json file)
        // and PeerAdmin.priv (copy from
        // cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv)
        User user = new SampleUser("c:\\tmp\\creds", "PeerAdmin");
        // "Log in"
        client.setUserContext(user);

        // Instantiate channel
        Channel channel = client.newChannel("mychannel");
        channel.addPeer(client.newPeer("peer", "grpc://192.168.99.100:7051"));
        // It always wants orderer, otherwise even query does not work
        channel.addOrderer(client.newOrderer("orderer", "grpc://192.168.99.100:7050"));
        channel.initialize();

        // below is querying and setting new owner

        String newOwner = "New Owner #" + new Random(new Date().getTime()).nextInt(999);
        System.out.println("New owner is '" + newOwner + "'\n");

        queryFabcar(channel, "CAR1");
        updateCarOwner(channel, "CAR1", newOwner, false);

        System.out.println("after request for transaction without commit");
        queryFabcar(channel, "CAR1");
        updateCarOwner(channel, "CAR1", newOwner, true);

        System.out.println("after request for transaction WITH commit");
        queryFabcar(channel, "CAR1");

        System.out.println("Sleeping 5s");
        Thread.sleep(5000); // 5secs
        queryFabcar(channel, "CAR1");
        System.out.println("all done");
    }

    private static void queryFabcar(Channel channel, String key) throws Exception {
        QueryByChaincodeRequest req = client.newQueryProposalRequest();
        ChaincodeID cid = ChaincodeID.newBuilder().setName("fabcar").build();
        req.setChaincodeID(cid);
        req.setFcn("queryCar");
        req.setArgs(new String[] { key });
        System.out.println("Querying for " + key);
        Collection<ProposalResponse> resps = channel.queryByChaincode(req);
        for (ProposalResponse resp : resps) {
            String payload = new String(resp.getChaincodeActionResponsePayload());
            System.out.println("response: " + payload);
        }

    }

    private static void updateCarOwner(Channel channel, String key, String newOwner, Boolean doCommit)
            throws Exception {
        TransactionProposalRequest req = client.newTransactionProposalRequest();
        ChaincodeID cid = ChaincodeID.newBuilder().setName("fabcar").build();
        req.setChaincodeID(cid);
        req.setFcn("changeCarOwner");
        req.setArgs(new String[] { key, newOwner });
        System.out.println("Executing for " + key);
        Collection<ProposalResponse> resps = channel.sendTransactionProposal(req);
        if (doCommit) {
            channel.sendTransaction(resps);
        }
    }

}

/***
 * Implementation of user. main business logic (as for fabcar example) is in
 * getEnrollment - get user's private key and cert
 * 
 */
class SampleUser implements User {
    private final String certFolder;
    private final String userName;

    public SampleUser(String certFolder, String userName) {
        this.certFolder = certFolder;
        this.userName = userName;
    }

    @Override
    public String getName() {
        return userName;
    }

    @Override
    public Set<String> getRoles() {
        return new HashSet<String>();
    }

    @Override
    public String getAccount() {
        return "";
    }

    @Override
    public String getAffiliation() {
        return "";
    }

    @Override
    public Enrollment getEnrollment() {
        return new Enrollment() {

            @Override
            public PrivateKey getKey() {
                try {
                    return loadPrivateKey(Paths.get(certFolder, userName + ".priv"));
                } catch (Exception e) {
                    return null;
                }
            }

            @Override
            public String getCert() {
                try {
                    return new String(Files.readAllBytes(Paths.get(certFolder, userName + ".cert")));
                } catch (Exception e) {
                    return "";
                }
            }

        };
    }

    @Override
    public String getMspId() {
        return "Org1MSP";
    }
    /***
     * loading private key from .pem-formatted file, ECDSA algorithm
     * (from some example on StackOverflow, slightly changed)
     * @param fileName - file with the key
     * @return Private Key usable
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public static PrivateKey loadPrivateKey(Path fileName) throws IOException, GeneralSecurityException {
        PrivateKey key = null;
        InputStream is = null;
        try {
            is = new FileInputStream(fileName.toString());
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            StringBuilder builder = new StringBuilder();
            boolean inKey = false;
            for (String line = br.readLine(); line != null; line = br.readLine()) {
                if (!inKey) {
                    if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
                        inKey = true;
                    }
                    continue;
                } else {
                    if (line.startsWith("-----END ") && line.endsWith(" PRIVATE KEY-----")) {
                        inKey = false;
                        break;
                    }
                    builder.append(line);
                }
            }
            //
            byte[] encoded = DatatypeConverter.parseBase64Binary(builder.toString());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
            KeyFactory kf = KeyFactory.getInstance("ECDSA");
            key = kf.generatePrivate(keySpec);
        } finally {
            is.close();
        }
        return key;
    }
}

1
嗨,Dmitry,感谢你的回答!你能否提供一个项目的Github链接,例如项目的完整设置。你使用了Maven和哪个版本的Java SDK? - branko terzic
关于JDK,它是jdk1.8.0_121。 - Dmitry Andrievsky
这个项目没有在GitHub上,它只是我在Fabric上的练习。它依赖于fabric-sdk-java,我从https://github.com/hyperledger/fabric-sdk-java.git获取了它。 - Dmitry Andrievsky
1
所有的Fabric设置都来自于fabcar示例,https://github.com/hyperledger/fabric-samples.git;./startFabric.sh完成所有设置。 - Dmitry Andrievsky
我在使用Java8时遇到了“java.security.NoSuchAlgorithmException: ECDSA KeyFactory not available”的问题。 - Neo
通过将 KeyFactory.getInstance("ECDSA"); 更改为 KeyFactory.getInstance("EC"); 解决了问题。 - Neo

12

您可以查看以下内容:

- Java SDK for Hyperledger Fabric 2.2。其中,在文件夹“fabric-sdk-java/src/test/java/org/hyperledger/fabric/sdkintegration/”中提供了两个文件==> End2endAndBackAgainIT.java,End2endIT.java。这可以帮助。

  • 有关演示,请参阅Youtube频道视频:端到端演示
  • 对于具有完整设置(网络和加密)的终端到终端演示的面料网络:E2E Cli设置

2020年6月7日更新

上述链接Java SDK for Hyperledger Fabric 2.2是与Hyperledger Fabric交互的低级SDK。

如果您的目的是构建Hyperledger Fabric区块链客户端应用程序,则建议使用Java版Hyperledger Fabric网关SDK,这是一个高级API。它非常简单易用,只需参考[2.2]中的代码片段即可。请参考链接如何使用
// code snippet from [2.2]
class Sample { 
public static void main(String[] args) throws IOException 
{ 
    // Load an existing wallet holding identities used to access the network. 
    Path walletDirectory = Paths.get("wallet"); 
    Wallet wallet = Wallets.newFileSystemWallet(walletDirectory); 
    // Path to a common connection profile describing the network. 
    Path networkConfigFile = Paths.get("connection.json"); 
    // Configure the gateway connection used to access the network.
    Gateway.Builder builder = Gateway.createBuilder() .identity(wallet, "user1").networkConfig(networkConfigFile); 

    // Create a gateway connection 
    try (Gateway gateway = builder.connect()){ 
        // Obtain a smart contract deployed on the network. 
        Network network = gateway.getNetwork("mychannel"); 
        Contract contract = network.getContract("fabcar"); 
        // Submit transactions that store state to the ledger. 
        byte[] createCarResult = contract.createTransaction("createCar").submit("CAR10", "VW", "Polo", "Grey","Mary"); 
        System.out.println(new String(createCarResult, StandardCharsets.UTF_8)); 
        // Evaluate transactions that query state from the ledger. 
        byte[] queryAllCarsResult = contract.evaluateTransaction("queryAllCars");
        System.out.println(new String(queryAllCarsResult, StandardCharsets.UTF_8)); 
    } 
    catch (ContractException | TimeoutException | InterruptedException e) { 
        e.printStackTrace(); 
    } 
} 

}

可获取1.42.2的API文档。


2
我认为这个Java示例比提供的链接更有帮助。它提供了一个无需冗余代码的端到端测试,展示了如何在纯Java环境下完成所有操作而无需使用CLI。 https://github.com/venugopv/FabricJavaSDKSample

嗨,玩家!我正在尝试让那段代码运行起来,虽然我已经启动了Docker镜像,但它并没有提供任何有用的调试信息,只是显示:$ docker-compose up -d Creating network "sdkintegration_default" with the default driver Creating sdkintegration_ccenv_1 ... Creating orderer.example.com ... Creating ca_peerOrg2 ... Creating ca_peerOrg1 ... Creating sdkintegration_ccenv_1 Creating orderer.example.com Creating ca_peerOrg2 Creating sdkintegration_ccenv_1 ... done - JGlass
当运行mvn packge时,我遇到了以下错误: Connect to localhost:7054 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect你能克服这个问题吗?我能够从这里获取的docker-compose正常工作 - 就连接而言 - 但其他示例却无法正常工作。非常感谢您的想法! - JGlass
没关系,玩家!我已经成功地让这个工作了 fabric-sdk-java - 谢谢! - JGlass

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接