Java tutorial
/* * * Copyright 2016,2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.hyperledger.fabric.sdkintegration; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.Set; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.hyperledger.fabric.sdk.BlockEvent.TransactionEvent; import org.hyperledger.fabric.sdk.BlockInfo; import org.hyperledger.fabric.sdk.Channel; import org.hyperledger.fabric.sdk.EventHub; import org.hyperledger.fabric.sdk.HFClient; import org.hyperledger.fabric.sdk.Peer; import org.hyperledger.fabric.sdk.TestConfigHelper; import org.hyperledger.fabric.sdk.UpdateChannelConfiguration; import org.hyperledger.fabric.sdk.security.CryptoSuite; import org.hyperledger.fabric.sdk.testutils.TestConfig; import org.junit.Before; import org.junit.Test; import static java.lang.String.format; import static org.hyperledger.fabric.sdk.Channel.PeerOptions.createPeerOptions; import static org.hyperledger.fabric.sdk.testutils.TestUtils.resetConfig; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Update channel scenario * See http://hyperledger-fabric.readthedocs.io/en/master/configtxlator.html * for details. */ public class UpdateChannelIT { private static final TestConfig testConfig = TestConfig.getConfig(); private static final String CONFIGTXLATOR_LOCATION = testConfig.getFabricConfigTxLaterLocation(); private static final String ORIGINAL_BATCH_TIMEOUT = "\"timeout\": \"2s\""; // Batch time out in configtx.yaml private static final String UPDATED_BATCH_TIMEOUT = "\"timeout\": \"5s\""; // What we want to change it to. private static final String FOO_CHANNEL_NAME = "foo"; private static final String PEER_0_ORG_1_EXAMPLE_COM_7051 = "peer0.org1.example.com:7051"; private static final String REGX_S_HOST_PEER_0_ORG_1_EXAMPLE_COM = "(?s).*\"host\":[ \t]*\"peer0\\.org1\\.example\\.com\".*"; private static final String REGX_S_ANCHOR_PEERS = "(?s).*\"*AnchorPeers\":[ \t]*\\{.*"; private final TestConfigHelper configHelper = new TestConfigHelper(); private Collection<SampleOrg> testSampleOrgs; @Before public void checkConfig() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, MalformedURLException { out("\n\n\nRUNNING: UpdateChannelIT\n"); resetConfig(); configHelper.customizeConfig(); // assertEquals(256, Config.getConfig().getSecurityLevel()); testSampleOrgs = testConfig.getIntegrationTestsSampleOrgs(); } @Test public void setup() { try { //////////////////////////// // Setup client //Create instance of client. HFClient client = HFClient.createNewInstance(); client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite()); //////////////////////////// //Set up USERS //Persistence is not part of SDK. Sample file store is for demonstration purposes only! // MUST be replaced with more robust application implementation (Database, LDAP) File sampleStoreFile = new File(System.getProperty("java.io.tmpdir") + "/HFCSampletest.properties"); sampleStoreFile.deleteOnExit(); final SampleStore sampleStore = new SampleStore(sampleStoreFile); //SampleUser can be any implementation that implements org.hyperledger.fabric.sdk.User Interface //////////////////////////// // get users for all orgs for (SampleOrg sampleOrg : testSampleOrgs) { final String orgName = sampleOrg.getName(); sampleOrg.setPeerAdmin(sampleStore.getMember(orgName + "Admin", orgName)); } //////////////////////////// //Reconstruct and run the channels SampleOrg sampleOrg = testConfig.getIntegrationTestsSampleOrg("peerOrg1"); Channel fooChannel = reconstructChannel(FOO_CHANNEL_NAME, client, sampleOrg); // Getting foo channels current configuration bytes. byte[] channelConfigurationBytes = fooChannel.getChannelConfigurationBytes(); HttpClient httpclient = HttpClients.createDefault(); // HttpPost httppost = new HttpPost(CONFIGTXLATOR_LOCATION + "/protolator/decode/common.Config"); // httppost.setEntity(new ByteArrayEntity(channelConfigurationBytes)); String responseAsString = configTxlatorDecode(httpclient, channelConfigurationBytes); //responseAsString is JSON but use just string operations for this test. if (!responseAsString.contains(ORIGINAL_BATCH_TIMEOUT)) { fail(format("Did not find expected batch timeout '%s', in:%s", ORIGINAL_BATCH_TIMEOUT, responseAsString)); } //Now modify the batch timeout String updateString = responseAsString.replace(ORIGINAL_BATCH_TIMEOUT, UPDATED_BATCH_TIMEOUT); HttpPost httppost = new HttpPost(CONFIGTXLATOR_LOCATION + "/protolator/encode/common.Config"); httppost.setEntity(new StringEntity(updateString)); HttpResponse response = httpclient.execute(httppost); int statuscode = response.getStatusLine().getStatusCode(); out("Got %s status for encoding the new desired channel config bytes", statuscode); assertEquals(200, statuscode); byte[] newConfigBytes = EntityUtils.toByteArray(response.getEntity()); // Now send to configtxlator multipart form post with original config bytes, updated config bytes and channel name. httppost = new HttpPost(CONFIGTXLATOR_LOCATION + "/configtxlator/compute/update-from-configs"); HttpEntity multipartEntity = MultipartEntityBuilder.create() .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) .addBinaryBody("original", channelConfigurationBytes, ContentType.APPLICATION_OCTET_STREAM, "originalFakeFilename") .addBinaryBody("updated", newConfigBytes, ContentType.APPLICATION_OCTET_STREAM, "updatedFakeFilename") .addBinaryBody("channel", fooChannel.getName().getBytes()).build(); httppost.setEntity(multipartEntity); response = httpclient.execute(httppost); statuscode = response.getStatusLine().getStatusCode(); out("Got %s status for updated config bytes needed for updateChannelConfiguration ", statuscode); assertEquals(200, statuscode); byte[] updateBytes = EntityUtils.toByteArray(response.getEntity()); UpdateChannelConfiguration updateChannelConfiguration = new UpdateChannelConfiguration(updateBytes); //To change the channel we need to sign with orderer admin certs which crypto gen stores: // private key: src/test/fixture/sdkintegration/e2e-2Orgs/channel/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/f1a9a940f57419a18a83a852884790d59b378281347dd3d4a88c2b820a0f70c9_sk //certificate: src/test/fixture/sdkintegration/e2e-2Orgs/channel/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem final String sampleOrgName = sampleOrg.getName(); final SampleUser ordererAdmin = sampleStore.getMember(sampleOrgName + "OrderAdmin", sampleOrgName, "OrdererMSP", Util.findFileSk(Paths.get("src/test/fixture/sdkintegration/e2e-2Orgs/" + testConfig.getFabricConfigGenVers() + "/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/") .toFile()), Paths.get("src/test/fixture/sdkintegration/e2e-2Orgs/" + testConfig.getFabricConfigGenVers() + "/crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem") .toFile()); client.setUserContext(ordererAdmin); //Ok now do actual channel update. fooChannel.updateChannelConfiguration(updateChannelConfiguration, client.getUpdateChannelConfigurationSignature(updateChannelConfiguration, ordererAdmin)); Thread.sleep(3000); // give time for events to happen //Let's add some additional verification... client.setUserContext(sampleOrg.getPeerAdmin()); final byte[] modChannelBytes = fooChannel.getChannelConfigurationBytes(); responseAsString = configTxlatorDecode(httpclient, modChannelBytes); if (!responseAsString.contains(UPDATED_BATCH_TIMEOUT)) { //If it doesn't have the updated time out it failed. fail(format("Did not find updated expected batch timeout '%s', in:%s", UPDATED_BATCH_TIMEOUT, responseAsString)); } if (responseAsString.contains(ORIGINAL_BATCH_TIMEOUT)) { //Should not have been there anymore! fail(format("Found original batch timeout '%s', when it was not expected in:%s", ORIGINAL_BATCH_TIMEOUT, responseAsString)); } assertTrue(eventCountFilteredBlock > 0); // make sure we got blockevent that were tested.updateChannelConfiguration assertTrue(eventCountBlock > 0); // make sure we got blockevent that were tested. //Should be no anchor peers defined. assertFalse(responseAsString.matches(REGX_S_HOST_PEER_0_ORG_1_EXAMPLE_COM)); assertFalse(responseAsString.matches(REGX_S_ANCHOR_PEERS)); // Get config update for adding an anchor peer. Channel.AnchorPeersConfigUpdateResult configUpdateAnchorPeers = fooChannel.getConfigUpdateAnchorPeers( fooChannel.getPeers().iterator().next(), sampleOrg.getPeerAdmin(), Arrays.asList(PEER_0_ORG_1_EXAMPLE_COM_7051), null); assertNotNull(configUpdateAnchorPeers.getUpdateChannelConfiguration()); assertTrue(configUpdateAnchorPeers.getPeersAdded().contains(PEER_0_ORG_1_EXAMPLE_COM_7051)); //Now add anchor peer to channel configuration. fooChannel.updateChannelConfiguration(configUpdateAnchorPeers.getUpdateChannelConfiguration(), client.getUpdateChannelConfigurationSignature( configUpdateAnchorPeers.getUpdateChannelConfiguration(), sampleOrg.getPeerAdmin())); Thread.sleep(3000); // give time for events to happen // Getting foo channels current configuration bytes to check with configtxlator channelConfigurationBytes = fooChannel.getChannelConfigurationBytes(); responseAsString = configTxlatorDecode(httpclient, channelConfigurationBytes); // Check is anchor peer in config block? assertTrue(responseAsString.matches(REGX_S_HOST_PEER_0_ORG_1_EXAMPLE_COM)); assertTrue(responseAsString.matches(REGX_S_ANCHOR_PEERS)); //Should see what's there. configUpdateAnchorPeers = fooChannel.getConfigUpdateAnchorPeers(fooChannel.getPeers().iterator().next(), sampleOrg.getPeerAdmin(), null, null); assertNull(configUpdateAnchorPeers.getUpdateChannelConfiguration()); // not updating anything. assertTrue(configUpdateAnchorPeers.getCurrentPeers().contains(PEER_0_ORG_1_EXAMPLE_COM_7051)); // peer should be there. assertTrue(configUpdateAnchorPeers.getPeersRemoved().isEmpty()); // not removing any assertTrue(configUpdateAnchorPeers.getPeersAdded().isEmpty()); // not adding anything. assertTrue(configUpdateAnchorPeers.getUpdatedPeers().isEmpty()); // not updating anyting. //Now remove the anchor peer -- get the config update block. configUpdateAnchorPeers = fooChannel.getConfigUpdateAnchorPeers(fooChannel.getPeers().iterator().next(), sampleOrg.getPeerAdmin(), null, Arrays.asList(PEER_0_ORG_1_EXAMPLE_COM_7051)); assertNotNull(configUpdateAnchorPeers.getUpdateChannelConfiguration()); assertTrue(configUpdateAnchorPeers.getCurrentPeers().contains(PEER_0_ORG_1_EXAMPLE_COM_7051)); // peer should still be there. assertTrue(configUpdateAnchorPeers.getPeersRemoved().contains(PEER_0_ORG_1_EXAMPLE_COM_7051)); // peer to remove. assertTrue(configUpdateAnchorPeers.getPeersAdded().isEmpty()); // not adding anything. assertTrue(configUpdateAnchorPeers.getUpdatedPeers().isEmpty()); // no peers should be left. // Now do the actual update. fooChannel.updateChannelConfiguration(configUpdateAnchorPeers.getUpdateChannelConfiguration(), client.getUpdateChannelConfigurationSignature( configUpdateAnchorPeers.getUpdateChannelConfiguration(), sampleOrg.getPeerAdmin())); Thread.sleep(3000); // give time for events to happen // Getting foo channels current configuration bytes to check with configtxlator. channelConfigurationBytes = fooChannel.getChannelConfigurationBytes(); responseAsString = configTxlatorDecode(httpclient, channelConfigurationBytes); assertFalse(responseAsString.matches(REGX_S_HOST_PEER_0_ORG_1_EXAMPLE_COM)); // should be gone! assertTrue(responseAsString.matches(REGX_S_ANCHOR_PEERS)); //ODDLY we still want this even if it's empty! //Should see what's there. configUpdateAnchorPeers = fooChannel.getConfigUpdateAnchorPeers(fooChannel.getPeers().iterator().next(), sampleOrg.getPeerAdmin(), null, null); assertNull(configUpdateAnchorPeers.getUpdateChannelConfiguration()); // not updating anything. assertTrue(configUpdateAnchorPeers.getCurrentPeers().isEmpty()); // peer should be now gone. assertTrue(configUpdateAnchorPeers.getPeersRemoved().isEmpty()); // not removing any assertTrue(configUpdateAnchorPeers.getPeersAdded().isEmpty()); // not adding anything. assertTrue(configUpdateAnchorPeers.getUpdatedPeers().isEmpty()); // no peers should be left out("That's all folks!"); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } } private String configTxlatorDecode(HttpClient httpclient, byte[] channelConfigurationBytes) throws IOException { HttpPost httppost = new HttpPost(CONFIGTXLATOR_LOCATION + "/protolator/decode/common.Config"); httppost.setEntity(new ByteArrayEntity(channelConfigurationBytes)); HttpResponse response = httpclient.execute(httppost); int statuscode = response.getStatusLine().getStatusCode(); // out("Got %s status for decoding current channel config bytes", statuscode); assertEquals(200, statuscode); return EntityUtils.toString(response.getEntity()); } int eventCountFilteredBlock = 0; int eventCountBlock = 0; private Channel reconstructChannel(String name, HFClient client, SampleOrg sampleOrg) throws Exception { client.setUserContext(sampleOrg.getPeerAdmin()); Channel newChannel = client.newChannel(name); for (String orderName : sampleOrg.getOrdererNames()) { newChannel.addOrderer(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName), testConfig.getOrdererProperties(orderName))); } assertTrue(sampleOrg.getPeerNames().size() > 1); // need at least two for testing. int i = 0; for (String peerName : sampleOrg.getPeerNames()) { String peerLocation = sampleOrg.getPeerLocation(peerName); Peer peer = client.newPeer(peerName, peerLocation, testConfig.getPeerProperties(peerName)); //Query the actual peer for which channels it belongs to and check it belongs to this channel Set<String> channels = client.queryChannels(peer); if (!channels.contains(name)) { throw new AssertionError(format("Peer %s does not appear to belong to channel %s", peerName, name)); } Channel.PeerOptions peerOptions = createPeerOptions() .setPeerRoles(EnumSet.of(Peer.PeerRole.CHAINCODE_QUERY, Peer.PeerRole.ENDORSING_PEER, Peer.PeerRole.LEDGER_QUERY, Peer.PeerRole.EVENT_SOURCE)); if (i % 2 == 0) { peerOptions.registerEventsForFilteredBlocks(); // we need a mix of each type for testing. } else { peerOptions.registerEventsForBlocks(); } ++i; newChannel.addPeer(peer, peerOptions); } for (String eventHubName : sampleOrg.getEventHubNames()) { EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName), testConfig.getEventHubProperties(eventHubName)); newChannel.addEventHub(eventHub); } //For testing of blocks which are not transactions. newChannel.registerBlockListener(blockEvent -> { // Note peer eventing will always start with sending the last block so this will get the last endorser block int transactions = 0; int nonTransactions = 0; for (BlockInfo.EnvelopeInfo envelopeInfo : blockEvent.getEnvelopeInfos()) { if (BlockInfo.EnvelopeType.TRANSACTION_ENVELOPE == envelopeInfo.getType()) { ++transactions; } else { assertEquals(BlockInfo.EnvelopeType.ENVELOPE, envelopeInfo.getType()); ++nonTransactions; } } assertTrue(format("nontransactions %d, transactions %d", nonTransactions, transactions), nonTransactions < 2); // non transaction blocks only have one envelope assertTrue(format("nontransactions %d, transactions %d", nonTransactions, transactions), nonTransactions + transactions > 0); // has to be one. assertFalse(format("nontransactions %d, transactions %d", nonTransactions, transactions), nonTransactions > 0 && transactions > 0); // can't have both. if (nonTransactions > 0) { // this is an update block -- don't care about others here. if (blockEvent.isFiltered()) { ++eventCountFilteredBlock; // make sure we're seeing non transaction events. } else { ++eventCountBlock; } assertEquals(0, blockEvent.getTransactionCount()); assertEquals(1, blockEvent.getEnvelopeCount()); for (TransactionEvent transactionEvent : blockEvent.getTransactionEvents()) { fail("Got transaction event in a block update"); // only events for update should not have transactions. } } }); newChannel.initialize(); return newChannel; } static void out(String format, Object... args) { System.err.flush(); System.out.flush(); System.out.println(format(format, args)); System.err.flush(); System.out.flush(); } }