diff --git a/src/main/java/hlf/java/rest/client/controller/FabricOperationsController.java b/src/main/java/hlf/java/rest/client/controller/FabricOperationsController.java index 1839d34..c9a0971 100644 --- a/src/main/java/hlf/java/rest/client/controller/FabricOperationsController.java +++ b/src/main/java/hlf/java/rest/client/controller/FabricOperationsController.java @@ -25,6 +25,18 @@ public class FabricOperationsController { @Autowired private SerializationUtil serializationUtil; + /** + * Get the anchor peers defined in a channel. + * + * @param channelName - the name of the channel for which you will retrieve the anchor peer nodes + * @return ResponseEntity - contains the list of anchor peers in a particular + * channel + */ + @GetMapping(value = "/channel/{channelName}/anchor_peers") + public ResponseEntity getAnchorPeerForChannel( + @PathVariable @Validated String channelName) { + return networkStatus.getAnchorPeerForChannel(channelName); + } /** * Obtain the channel configuration details. * diff --git a/src/main/java/hlf/java/rest/client/service/NetworkStatus.java b/src/main/java/hlf/java/rest/client/service/NetworkStatus.java index 7d0fdd3..ee0cf96 100644 --- a/src/main/java/hlf/java/rest/client/service/NetworkStatus.java +++ b/src/main/java/hlf/java/rest/client/service/NetworkStatus.java @@ -9,6 +9,8 @@ public interface NetworkStatus { ResponseEntity getChannelFromNetwork(String channelName); + ResponseEntity getAnchorPeerForChannel(String channelName); + ResponseEntity signChannelConfigTransaction( String channelName, String configUpdate); diff --git a/src/main/java/hlf/java/rest/client/service/impl/NetworkStatusImpl.java b/src/main/java/hlf/java/rest/client/service/impl/NetworkStatusImpl.java index 93c96d1..500149c 100644 --- a/src/main/java/hlf/java/rest/client/service/impl/NetworkStatusImpl.java +++ b/src/main/java/hlf/java/rest/client/service/impl/NetworkStatusImpl.java @@ -7,6 +7,7 @@ import hlf.java.rest.client.exception.ErrorCode; import hlf.java.rest.client.exception.ErrorConstants; import hlf.java.rest.client.exception.FabricTransactionException; +import hlf.java.rest.client.exception.NotFoundException; import hlf.java.rest.client.exception.ServiceException; import hlf.java.rest.client.model.AnchorPeerDTO; import hlf.java.rest.client.model.ChannelUpdateParamsDTO; @@ -15,16 +16,24 @@ import hlf.java.rest.client.service.ChannelConfigDeserialization; import hlf.java.rest.client.service.NetworkStatus; import hlf.java.rest.client.service.UpdateChannel; +import hlf.java.rest.client.util.FabricClientConstants; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Base64; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.hyperledger.fabric.gateway.Gateway; import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.protos.common.Configtx; import org.hyperledger.fabric.protos.common.Configtx.Config; import org.hyperledger.fabric.protos.common.Configtx.ConfigGroup; import org.hyperledger.fabric.protos.common.Configtx.ConfigUpdate; +import org.hyperledger.fabric.protos.peer.Configuration; import org.hyperledger.fabric.sdk.Channel; import org.hyperledger.fabric.sdk.UpdateChannelConfiguration; import org.hyperledger.fabric.sdk.User; @@ -55,6 +64,73 @@ private String getDeserializedConfig(MessageOrBuilder message) { return Base64.getEncoder().encodeToString(channelConfigString.getBytes()); } + @Override + public ResponseEntity getAnchorPeerForChannel(String channelName) { + Network network = gateway.getNetwork(channelName); + try { + Set anchorPeersSet = new HashSet<>(); + if (network != null) { + Channel selectedChannel = network.getChannel(); + byte[] selectedChannelBytes = selectedChannel.getChannelConfigurationBytes(); + log.debug("peers: {} ", selectedChannel.getPeers()); + Configtx.Config.Builder configBuilderUpdate = + Config.parseFrom(selectedChannelBytes).toBuilder(); + ConfigGroup.Builder channelGroupBuild = configBuilderUpdate.getChannelGroup().toBuilder(); + Map groupsMap = channelGroupBuild.getGroupsMap(); + ConfigGroup.Builder application = + groupsMap.get(FabricClientConstants.CHANNEL_CONFIG_GROUP_APPLICATION).toBuilder(); + // Get all MSP IDs for a particular channel + Collection peersOrganizationMSPIDs = selectedChannel.getPeersOrganizationMSPIDs(); + log.debug("peersOrganizationMSPIDs: {}", peersOrganizationMSPIDs.toString()); + for (String peerOrganizationMSPID : peersOrganizationMSPIDs) { + ConfigGroup peerOrgConfigGroup = application.getGroupsMap().get(peerOrganizationMSPID); + if (peerOrgConfigGroup != null) { + ConfigGroup.Builder peerOrgConfigGroupBuilder = peerOrgConfigGroup.toBuilder(); + Map valuesMap = peerOrgConfigGroupBuilder.getValuesMap(); + Configtx.ConfigValue anchorPeers = + valuesMap.get(FabricClientConstants.CHANNEL_CONFIG_GROUP_VALUE_ANCHORPEERS); + if (null != anchorPeers && anchorPeers.getValue() != null) { + Configuration.AnchorPeers anchorPeersValue = + Configuration.AnchorPeers.parseFrom(anchorPeers.getValue()); + List anchorPeersList = + anchorPeersValue.getAnchorPeersList(); + if (anchorPeersList != null) { + for (Configuration.AnchorPeer anchorPeer : anchorPeersList) { + // Concatenating the host and port to form a URL + anchorPeersSet.add(anchorPeer.getHost() + ":" + anchorPeer.getPort()); + } + } + } + } + } + return new ResponseEntity<>( + new ClientResponseModel(ErrorConstants.NO_ERROR, (Serializable) anchorPeersSet), + HttpStatus.OK); + } else { + log.warn("Error retrieving anchor peers: Network cannot be NULL"); + throw new NotFoundException(ErrorCode.NOT_FOUND, "Network cannot be NULL"); + } + + } catch (InvalidArgumentException e) { + log.warn( + "Error while fetching channel config: Channel has no peer or orderer defined. Can not get configuration block"); + throw new ServiceException( + ErrorCode.HYPERLEDGER_FABRIC_TRANSACTION_ERROR, + "Channel has no peer or orderer defined. Can not get configuration block", + e); + } catch (TransactionException e) { + log.warn("Error retrieving anchor peers: {} ", e.getMessage()); + throw new FabricTransactionException( + ErrorCode.HYPERLEDGER_FABRIC_TRANSACTION_ERROR, + ErrorCode.HYPERLEDGER_FABRIC_TRANSACTION_ERROR.name(), + e); + } catch (InvalidProtocolBufferException e) { + log.warn("Error retrieving anchor peers: {}", e.getMessage()); + throw new ServiceException( + ErrorCode.HYPERLEDGER_FABRIC_TRANSACTION_ERROR, "Error retrieving anchor peers", e); + } + } + @Override /** * Obtains the channel configuration diff --git a/src/test/java/hlf/java/rest/client/IT/ChannelIT.java b/src/test/java/hlf/java/rest/client/IT/ChannelIT.java index 4675c33..63e0af3 100644 --- a/src/test/java/hlf/java/rest/client/IT/ChannelIT.java +++ b/src/test/java/hlf/java/rest/client/IT/ChannelIT.java @@ -353,4 +353,12 @@ public void addAnchorPeersToChannelTest() { networkStatus.addAnchorPeersToChannel(CHANNEL_NAME, channelUpdateParamsDTO); Assertions.assertEquals(new Integer(200), responseModel.getStatusCodeValue()); } + + @Test + @Order(8) + public void getAnchorPeerForChannelTest() { + ResponseEntity responseModel = + networkStatus.getAnchorPeerForChannel(CHANNEL_NAME); + Assertions.assertEquals(new Integer(200), responseModel.getStatusCodeValue()); + } } diff --git a/src/test/java/hlf/java/rest/client/controller/FabricOperationsControllerTest.java b/src/test/java/hlf/java/rest/client/controller/FabricOperationsControllerTest.java index e326e75..e90b75c 100644 --- a/src/test/java/hlf/java/rest/client/controller/FabricOperationsControllerTest.java +++ b/src/test/java/hlf/java/rest/client/controller/FabricOperationsControllerTest.java @@ -24,6 +24,15 @@ public class FabricOperationsControllerTest { @Mock private NetworkStatus networkStatus; @Mock private SerializationUtil serializationUtil; + @Test + public void getAnchorPeerForChannelTest() { + ResponseEntity responseEntity = new ResponseEntity(HttpStatus.OK); + Mockito.when(networkStatus.getAnchorPeerForChannel(Mockito.anyString())) + .thenReturn(responseEntity); + assertEquals( + responseEntity, fabricOperationsController.getAnchorPeerForChannel("some_channel_name")); + } + @Test public void getChannelConfigurationTest() { ResponseEntity responseEntity = new ResponseEntity(HttpStatus.OK); diff --git a/src/test/java/hlf/java/rest/client/service/impl/NetworkStatusImplTest.java b/src/test/java/hlf/java/rest/client/service/impl/NetworkStatusImplTest.java index eaefa11..321667a 100644 --- a/src/test/java/hlf/java/rest/client/service/impl/NetworkStatusImplTest.java +++ b/src/test/java/hlf/java/rest/client/service/impl/NetworkStatusImplTest.java @@ -15,11 +15,15 @@ import hlf.java.rest.client.model.CommitChannelParamsDTO; import hlf.java.rest.client.model.MSPDTO; import hlf.java.rest.client.service.ChannelConfigDeserialization; +import hlf.java.rest.client.util.FabricClientConstants; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; import org.hyperledger.fabric.gateway.Network; import org.hyperledger.fabric.gateway.impl.GatewayImpl; +import org.hyperledger.fabric.protos.common.Configtx; import org.hyperledger.fabric.protos.common.Configtx.Config; import org.hyperledger.fabric.protos.common.Configtx.ConfigGroup; import org.hyperledger.fabric.protos.common.Configtx.ConfigUpdate; @@ -89,6 +93,33 @@ public class NetworkStatusImplTest { @Mock private ByteString byteString; @Mock private ConfigUpdate.Builder configUpdateBuilder; + @Mock private ConfigGroup.Builder configGroupBuilder; + + @Mock private Configtx.Config.Builder configtxConfigBuilder; + @Mock private Configtx.Config configTxConfig; + @Mock private Configtx.ConfigGroup configTxConfigGroup; + + @Mock private Map groupMap; + + @Test + public void getAnchorPeerForChannelTest() throws InvalidArgumentException, TransactionException { + ResponseEntity responseEntity = + new ResponseEntity<>( + new ClientResponseModel(ErrorConstants.NO_ERROR, new HashSet<>()), HttpStatus.OK); + Mockito.when(gateway.getNetwork(Mockito.anyString())).thenReturn(network); + Mockito.when(network.getChannel()).thenReturn(channel); + Mockito.when(channel.getChannelConfigurationBytes()).thenReturn(new byte[0]); + staticConfig.when(() -> Config.parseFrom(Mockito.any(byte[].class))).thenReturn(configTxConfig); + Mockito.when(configTxConfig.toBuilder()).thenReturn(configtxConfigBuilder); + Mockito.when(configtxConfigBuilder.getChannelGroup()).thenReturn(configTxConfigGroup); + Mockito.when(configTxConfigGroup.toBuilder()).thenReturn(configGroupBuilder); + Mockito.when(configGroupBuilder.getGroupsMap()).thenReturn(groupMap); + Mockito.when(groupMap.get(FabricClientConstants.CHANNEL_CONFIG_GROUP_APPLICATION)) + .thenReturn(readset); + assertEquals( + responseEntity.getStatusCode(), + networkStatus.getAnchorPeerForChannel("some_channelname").getStatusCode()); + } @Test public void getChannelFromNetworkTest()