淘先锋技术网

首页 1 2 3 4 5 6 7

运行测试Demo——End2endIT.java

运行结果分析

1. 先构建通道foo,把org1加入该通道,运行该通道


run-channel.png

2. 初始化a为100,b为200,并把此交易送至orderer中


set-a-b.png

3. a转账100给b,背书后,把交易送至orderer


a-100to-b.png

4. 查询b的值


query-b.png

5. 后面就是创建调用另一个通道bar,进行相同的操作,这里也不详细罗列了

End2endIT源码分析(注释都在代码中)

1. 先执行checkConfig()方法检查配置项

  @Before
    public void checkConfig() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, MalformedURLException, org.hyperledger.fabric_ca.sdk.exception.InvalidArgumentException {
        out("\n\n\nRUNNING: %s.\n", testName);
        resetConfig();//先重置配置文件org.hyperledger.fabric.sdk.helper.Config,config主要是fabric需要的配置项
        configHelper.customizeConfig();//调用命令行输入的指定变量,覆盖掉上面的Config配置

        testSampleOrgs = testConfig.getIntegrationTestsSampleOrgs();//获取testConfig配置的组织,TestConfig在End2endIT已经被new出来,里面也有大量的测试配置项
        for (SampleOrg sampleOrg : testSampleOrgs) {//设置颁发证书的CA证书机构
            String caName = sampleOrg.getCAName(); 
            if (caName != null && !caName.isEmpty()) {
                sampleOrg.setCAClient(HFCAClient.createNewInstance(caName, sampleOrg.getCALocation(), sampleOrg.getCAProperties()));
            } else {
                sampleOrg.setCAClient(HFCAClient.createNewInstance(sampleOrg.getCALocation(), sampleOrg.getCAProperties()));
            }
        }
    }

 

2. 执行setup()方法

  @Test
    public void setup() throws Exception {
        if (sampleStoreFile.exists()) {
            sampleStoreFile.delete();//模拟数据库存储使用了HFCSampletest.properties
        }
        sampleStore = new SampleStore(sampleStoreFile);//初始化存储
        enrollUsersSetup(sampleStore);// 利用ca做初始化
        runFabricTest(sampleStore);//核心方法

    }

3. 分析enrollUsersSetup()方法

  for (SampleOrg sampleOrg : testSampleOrgs) {

            HFCAClient ca = sampleOrg.getCAClient();//获得ca客户端

            final String orgName = sampleOrg.getName();//组织名称
            final String mspid = sampleOrg.getMSPID();//组织证书ID
            ca.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());//设置CA客户端证书套件

            if (testConfig.isRunningFabricTLS()) {
                //从CA客户端获取TLS证书
                final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
                enrollmentRequestTLS.addHost("localhost");
                enrollmentRequestTLS.setProfile("tls");
                final Enrollment enroll = ca.enroll("admin", "adminpw", enrollmentRequestTLS);
                final String tlsCertPEM = enroll.getCert();
                final String tlsKeyPEM = getPEMStringFromPrivateKey(enroll.getKey());

                final Properties tlsProperties = new Properties();

                tlsProperties.put("clientKeyBytes", tlsKeyPEM.getBytes(UTF_8));
                tlsProperties.put("clientCertBytes", tlsCertPEM.getBytes(UTF_8));
                clientTLSProperties.put(sampleOrg.getName(), tlsProperties);

                sampleStore.storeClientPEMTLCertificate(sampleOrg, tlsCertPEM);
                sampleStore.storeClientPEMTLSKey(sampleOrg, tlsKeyPEM);
            }
            //检查是否连接成功
            HFCAInfo info = ca.info();
            assertNotNull(info);
            String infoName = info.getCAName();
            if (infoName != null && !infoName.isEmpty()) {
                assertEquals(ca.getCAName(), infoName);
            }
            //获取组织管理员(admin)
            SampleUser admin = sampleStore.getMember(TEST_ADMIN_NAME, orgName);
            if (!admin.isEnrolled()) { 
                //管理员只需在CA客户端进行登记
                admin.setEnrollment(ca.enroll(admin.getName(), "adminpw"));
                admin.setMspId(mspid);
            }
            //获取普通用户
            SampleUser user = sampleStore.getMember(testUser1, sampleOrg.getName());
            if (!user.isRegistered()) { 
                //如果未注册则生成注册请求
                RegistrationRequest rr = new RegistrationRequest(user.getName(), "org1.department1");
                //设置密码(注册请求发出去后,CA会返回一个密码)
                user.setEnrollmentSecret(ca.register(rr, admin));
            }
            if (!user.isEnrolled()) {
                //如果未登记则登记
                user.setEnrollment(ca.enroll(user.getName(), user.getEnrollmentSecret()));
                user.setMspId(mspid);
            }

            final String sampleOrgName = sampleOrg.getName();
            final String sampleOrgDomainName = sampleOrg.getDomainName();//获取组织的命名域
            //使用证书文件生成用户信息
            SampleUser peerOrgAdmin = sampleStore.getMember(sampleOrgName + "Admin", sampleOrgName, sampleOrg.getMSPID(),
                    Util.findFileSk(Paths.get(testConfig.getTestChannelPath(), "crypto-config/peerOrganizations/",
                            sampleOrgDomainName, format("/users/Admin@%s/msp/keystore", sampleOrgDomainName)).toFile()),
                    Paths.get(testConfig.getTestChannelPath(), "crypto-config/peerOrganizations/", sampleOrgDomainName,
                            format("/users/Admin@%s/msp/signcerts/Admin@%s-cert.pem", sampleOrgDomainName, sampleOrgDomainName)).toFile());
          
            sampleOrg.setPeerAdmin(peerOrgAdmin);//设置节点管理员(一个特殊的用户,可以创建通道,加入节点以及安装链码)

            sampleOrg.addUser(user);//组织内添加用户
            sampleOrg.setAdmin(admin); //设置管理员身份
        }

4. 分析 runFabricTest()方法

 
        // Setup client

        //Create instance of client.
        HFClient client = HFClient.createNewInstance();//初始化一个客户端,类似cli

        client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());//设置加密算法

        
        //Construct and run the channels
        // 获取peerOrg1组织1
        SampleOrg sampleOrg = testConfig.getIntegrationTestsSampleOrg("peerOrg1");
        // 构建一个channel通道,Org1加入到该通道中
        Channel fooChannel = constructChannel(FOO_CHANNEL_NAME, client, sampleOrg);
        // 保存通道名称到数据库中(这里是存储到上面方法文件)
        sampleStore.saveChannel(fooChannel);
        // 安装链码、实例化链码、执行一个查询测试
        runChannel(client, fooChannel, true, sampleOrg, 0);

        assertFalse(fooChannel.isShutdown());
        fooChannel.shutdown(true); // Force foo channel to shutdown clean up resources.
        assertTrue(fooChannel.isShutdown());

        assertNull(client.getChannel(FOO_CHANNEL_NAME));
        out("\n");
        // 下面是组织2的过程,跟组织1是类似的
        sampleOrg = testConfig.getIntegrationTestsSampleOrg("peerOrg2");
        Channel barChannel = constructChannel(BAR_CHANNEL_NAME, client, sampleOrg);
        assertTrue(barChannel.isInitialized());
        /**
         * sampleStore.saveChannel uses {@link Channel#serializeChannel()}
         */
        sampleStore.saveChannel(barChannel);
        assertFalse(barChannel.isShutdown());
        runChannel(client, barChannel, true, sampleOrg, 100); //run a newly constructed bar channel with different b value!
        //let bar channel just shutdown so we have both scenarios.

        out("\nTraverse the blocks for chain %s ", barChannel.getName());
        // 对区块进行各种查询,包括区块读写集、区块数量高度等
        blockWalker(client, barChannel);

        assertFalse(barChannel.isShutdown());
        assertTrue(barChannel.isInitialized());
        out("That's all folks!");

5. 分析constructChannel()方法(重点1)

a. 获取peer的admin用户,实际上是crytogen根据crypto-config.yaml配置的默认admin账户

    SampleUser peerAdmin = sampleOrg.getPeerAdmin();
        client.setUserContext(peerAdmin);

b. 接着是初始化orderer排序节点对象

  Collection<Orderer> orderers = new LinkedList<>();

        for (String orderName : sampleOrg.getOrdererNames()) {

            Properties ordererProperties = testConfig.getOrdererProperties(orderName);

            //example of setting keepAlive to avoid timeouts on inactive http2 connections.
            // Under 5 minutes would require changes to server side to accept faster ping rates.
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[] {true});

            orderers.add(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName),
                    ordererProperties));
        }

c. 创建一个channel,这里使用的是Configtxgen 生成的“通道配置”文件

//Just pick the first orderer in the list to create the channel. 
  Orderer anOrderer = orderers.iterator().next();//选择第一个排序节点进行创建通道 
  orderers.remove(anOrderer); //从排序节点集合中移除已经在使用的节点

//通过读取channel.tx文件,生成信道配置对象 
  ChannelConfiguration channelConfiguration = new ChannelConfiguration(new File(TEST_FIXTURES_PATH + "/sdkintegration/e2e-2Orgs/channel/" + name + ".tx")); 

//创建信道对象,只有一个签名,就是组织节点管理员。如果创建信道策略需要更多的签名,那么他们必须添加
  Channel newChannel = client.newChannel(name, anOrderer, channelConfiguration, client.getChannelConfigurationSignature(channelConfiguration, sampleOrg.getPeerAdmin()));
  out("Created channel %s", name);

d. client.newPeer创建peer节点,然后joinPeer加入通道channel中

   boolean everyother = true; //test with both cases when doing peer eventing.
        for (String peerName : sampleOrg.getPeerNames()) {
            String peerLocation = sampleOrg.getPeerLocation(peerName);//获取节点位置

            Properties peerProperties = testConfig.getPeerProperties(peerName); //获取节点属性
            if (peerProperties == null) {
                peerProperties = new Properties();
            }

            //Example of setting specific options on grpc's NettyChannelBuilder
            peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);

            Peer peer = client.newPeer(peerName, peerLocation, peerProperties);//通过客户端生成节点对象
            if (testConfig.isFabricVersionAtOrAfter("1.3")) {
                //加入生成的节点
                newChannel.joinPeer(peer, createPeerOptions().setPeerRoles(EnumSet.of(PeerRole.ENDORSING_PEER, PeerRole.LEDGER_QUERY, PeerRole.CHAINCODE_QUERY, PeerRole.EVENT_SOURCE))); //Default is all roles.

            } else {
                if (doPeerEventing && everyother) {
                    newChannel.joinPeer(peer, createPeerOptions().setPeerRoles(EnumSet.of(PeerRole.ENDORSING_PEER, PeerRole.LEDGER_QUERY, PeerRole.CHAINCODE_QUERY, PeerRole.EVENT_SOURCE))); //Default is all roles.
                } else {
                    // Set peer to not be all roles but eventing.
                    newChannel.joinPeer(peer, createPeerOptions().setPeerRoles(EnumSet.of(PeerRole.ENDORSING_PEER, PeerRole.LEDGER_QUERY, PeerRole.CHAINCODE_QUERY)));
                }
            }
            out("Peer %s joined channel %s", peerName, name);
            everyother = !everyother;
        }

e. 给channel设置监听事件的grpc接口,最后进行初始化

  //获取组织内事件记录节点集合
    for (String eventHubName : sampleOrg.getEventHubNames()) {
            //获取事件记录属性
            final Properties eventHubProperties = testConfig.getEventHubProperties(eventHubName);

            eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
            eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});

            EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName),
                    eventHubProperties);
            newChannel.addEventHub(eventHub);
        }
        //信道实例化
        newChannel.initialize();

        out("Finished initialization channel %s", name);

6.分析runChannel()方法(重点2

a. 安装链码

 if (installChaincode) {

               //一大段设置安装的链码信息
               // .......
               //发送安装链码的交易提案
                responses = client.sendInstallProposal(installProposalRequest, peers);

               //检查返回的结果,并输出相应的信息
               //.......

b. 实例化链码,设置背书策略

     //实例化链码
            InstantiateProposalRequest instantiateProposalRequest = client.newInstantiationProposalRequest();
            instantiateProposalRequest.setProposalWaitTime(DEPLOYWAITTIME);
            instantiateProposalRequest.setChaincodeID(chaincodeID);
            instantiateProposalRequest.setChaincodeLanguage(CHAIN_CODE_LANG);
            instantiateProposalRequest.setFcn("init");
            instantiateProposalRequest.setArgs(new String[] {"a", "500", "b", "" + (200 + delta)});
            Map<String, byte[]> tm = new HashMap<>();
            tm.put("HyperLedgerFabric", "InstantiateProposalRequest:JavaSDK".getBytes(UTF_8));
            tm.put("method", "InstantiateProposalRequest".getBytes(UTF_8));
            instantiateProposalRequest.setTransientMap(tm);

            /*
              policy OR(Org1MSP.member, Org2MSP.member) meaning 1 signature from someone in either Org1 or Org2
              See README.md Chaincode endorsement policies section for more details.
            */
            //设置背书策略
            ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
            chaincodeEndorsementPolicy.fromYamlFile(new File(TEST_FIXTURES_PATH + "/sdkintegration/chaincodeendorsementpolicy.yaml"));
            instantiateProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);

            out("Sending instantiateProposalRequest to all peers with arguments: a and b set to 100 and %s respectively", "" + (200 + delta));
            successful.clear();
            failed.clear();

            //检查结果,并输出相应信息

c. 转账,查询,异常处理

channel.sendTransaction(successful, createTransactionOptions()
                    .userContext(client.getUserContext())
                    .shuffleOrders(false)
                    .orderers(channel.getOrderers())
                    .nOfEvents(nOfEvents)
            ).thenApply(transactionEvent -> {
            //.......
            // 调用example_cc.go里面的move方法,a给b转账

}.thenApply(transactionEvent -> {
                    //........
                    // query查询b的余额

                try {
}).exceptionally(e -> {
                    //........
                    // 异常处理
}

d. 查询区块高度,信息之类的