Extending Fabric 2 Test Network
Tags: blockchain, hyperledgerfabric, distributedsystems, fabric
Published: 2020-06-25 19:55:22

**What is Fabric 2**
Hyperledger Fabric is a private blockchain platform that enables you to create permissioned blockchain network. The main elements in Hyperledger Fabric are:
- **Peer & Organization**: Peer node hosts and execute ledger transactions and smart contracts. Peer belongs to Organization. Some peers can be endorsing peers.
- **Orderer**: Orderer provides ordering service (packages transactions into blocks to be delivered to peers on a channel).
- **Channel**: Channel are logical structure formed by collection of peers.
- **Chaincode / Smart Contract**: Smart Contract is software that defines the business logic, contains assets and related transactions. Hyperledger Fabric smart contracts are called chaincode and may specify an endorsement policy which defines transaction validity.

The approach that is taken by Hyperledger Fabric (in my opinion) is beneficial for industrial use cases because it enables cooperation between organization (using channels) while to certain extent having a privacy inside the organization.

To explore the capabilities of Fabric, there are some examples provided that serves certain use cases. For a simple introduction, there are 2 examples that should be used: First Network and Test Network. Due to a major upgrade in Hyperledger Fabric, there are 2 major versions available 1.4.x and 2.x.x - First Network is suitable if you want to learn Fabric 1.4.x but Test Network would be preferable for Fabric 2.x.x. Also good to know that Test Network is a replacement for First Network so you should use that whenever possible.

The striking difference between First Network and Test Network is the number of nodes. First Networks comes with 2 peers for each organization, and 5 orderers while Test Network only comes with 1 peer per organization and 1 orderer. This might be limited for some scenario that you want to run. This article will help you extend the test network with some helpful bash scripts. The underlying assumption is that you already have a Test Network running with command `./network.sh up createChannel && ./network.sh deployCC`

### TL;DR
To extend Test Network, we need to extend fabric key with `cryptogen`, update `system-channel` definition, compose orderer nodes, compose peer nodes, join application channel and deploy the chaincode. For an easy setup, clone [this repository](https://github.com/aqidd/fabric-test-network-extended) and run `./extend-network.sh` after setting up your local Test Network. Or to make things easier, just run `./setup-fabric-extended.sh` that will setup local Test Network as well as extends it.

---
**Extending Fabric Key**
Before extending the network, we need to generate all the necessary key for each new orderer and peer that we want to create. The easiest way to do that is by using `cryptogen` command. `cryptogen` is an utility for generating Hyperledger Fabric key material. It is provided as a means of preconfiguring a network for testing purposes. It would normally not be used in the operation of a production network.

```shell
cryptogen extend --config=crypto-config.yaml --input="organizations"
```
The `--config` parameter should point to the new network configuration, not the existing test network configuration. In this case, we want a total of 3 orderers, with 3 peers for each organization.
```yaml
OrdererOrgs:
- Name: Orderer
  Domain: example.com
  EnableNodeOUs: true

  Specs:
    - Hostname: orderer
    - Hostname: orderer1
    - Hostname: orderer2

PeerOrgs:
- Name: Org1
  Domain: org1.example.com
  EnableNodeOUs: true

  Template:
      Count: 3
      SANS:
        - localhost
  Users:
      Count: 1

- Name: Org2
  Domain: org2.example.com
  EnableNodeOUs: true

  Template:
      Count: 3
      SANS:
        - localhost
  Users:
      Count: 1
```

**Extending Test Network Orderer**
First, we need to update `system-channel` configuration so that we can run new orderer node. The steps needed to update `system-channel` are as follows:
1. Fetch (and decode) latest configuration
2. Add new orderer certificate generated by `cryptogen` to orderer consenters and modify the configuration
3. Apply the change to blockchain

To fetch and decode latest configuration, we need to execute:
```shell
peer channel fetch config ./out/config_block.pb -o $ORDERER_ADDRESS -c $2 --tls --cafile $TLS_ROOT_CA

configtxlator proto_decode --input ./out/config_block.pb --type common.Block --output ./out/config_block.json

jq .data.data[0].payload.data.config ./out/config_block.json > ./out/config.json
```
The commands above will get the latest config and store the result in `out` folder. The default orderer address is `export ORDERER_ADDRESS=localhost:7050` and the `TLS_ROOT_CA`should be `test-network/organizations/ordererOrganizations/example.com/msp/tlscacerts/tlsca.example.com-cert.pem`

Next is adding new orderer certificate to the new configuration. We can achieve that by executing commands below:
```shell
# add new orderer certificate
echo "{\"client_tls_cert\":\"$(cat $ORDERER_TLS | base64 -w 0)\",\"host\":\"orderer$1.example.com\",\"port\":7$150,\"server_tls_cert\":\"$(cat $ORDERER_TLS | base64 -w 0)\"}" > $PWD/out/ord$1consenter.json

jq ".channel_group.groups.Orderer.values.ConsensusType.value.metadata.consenters += [$(cat ./out/ord$1consenter.json)]" ./out/config.json > ./out/modified_config.json

# calculate config differences
configtxlator proto_encode --input ./out/config.json --type common.Config --output ./out/config.pb

configtxlator proto_encode --input ./out/modified_config.json --type common.Config --output ./out/modified_config.pb

configtxlator compute_update --channel_id $2 --original ./out/config.pb --updated ./out/modified_config.pb --output ./out/config_update.pb
```
`$1` refers to our orderer address, `$2` refer to channel name (system-channel) and `$ORDERER_TLS` should refer to TLS certificate for the corresponding orderer.

The final step before starting the node is applying the changes via update transaction
```shell
# "applying changes"
configtxlator proto_decode --input ./out/config_update.pb --type common.ConfigUpdate --output ./out/config_update.json

echo '{"payload":{"header":{"channel_header":{"channel_id":"'$2'", "type":2}},"data":{"config_update":'$(cat ./out/config_update.json)'}}}' | jq . > ./out/config_update_in_envelope.json

configtxlator proto_encode --input ./out/config_update_in_envelope.json --type common.Envelope --output ./out/config_update_in_envelope.pb

# "submit update transaction"
peer channel update -f ./out/config_update_in_envelope.pb -c $2 -o $ORDERER_ADDRESS --tls --cafile $TLS_ROOT_CA
```

The final step is actually creating and running the orderer node. The easiest way is to use docker compose and add the service definition below
```yaml
  orderer1.example.com:
    container_name: orderer1.example.com
    extends: 
      file: orderer-base.yaml
      service: orderer-base
    environment: 
      - ORDERER_GENERAL_LISTENPORT=7150
    volumes:
      - ${FABRIC_SAMPLES_DIR}/test-network/system-genesis-block/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
      - ${FABRIC_SAMPLES_DIR}/test-network/organizations/ordererOrganizations/example.com/orderers/orderer1.example.com/msp:/var/hyperledger/orderer/msp
      - ${FABRIC_SAMPLES_DIR}/test-network/organizations/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/:/var/hyperledger/orderer/tls
      - orderer1.example.com:/var/hyperledger/production/orderer
    ports:
      - 7150:7150
```
with `orderer-base.yaml` defined below
```yaml
version: '2'

services:
  orderer-base:
      image: hyperledger/fabric-orderer:$IMAGE_TAG
      environment:
        - FABRIC_LOGGING_SPEC=INFO
        - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
        - ORDERER_GENERAL_GENESISMETHOD=file
        - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
        - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
        - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
        # enabled TLS
        - ORDERER_GENERAL_TLS_ENABLED=true
        - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
        - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
        - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
        - ORDERER_KAFKA_TOPIC_REPLICATIONFACTOR=1
        - ORDERER_KAFKA_VERBOSE=true
        - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt
        - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key
        - ORDERER_GENERAL_CLUSTER_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
      working_dir: /opt/gopath/src/github.com/hyperledger/fabric
      command: orderer
      networks:
        - net_test # docker network for Test Network
```

**Extending Test Network Peer**
Extending peer node is easier. After you've generated all the required key, you can directly compose the node. One of the sample configuration for your node container is given below
```yaml
  peer1.org1.example.com:
    container_name: peer1.org1.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      # Peer specific variabes
      - CORE_PEER_ID=peer1.org1.example.com
      - CORE_PEER_ADDRESS=peer1.org1.example.com:7151
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7151
      - CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:7152
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7152
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:7151
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:7151
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
      - /var/run/:/host/var/run/
      - ${FABRIC_SAMPLES_DIR}/test-network/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp
      - ${FABRIC_SAMPLES_DIR}/test-network/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls
      - peer1.org1.example.com:/var/hyperledger/production
    ports:
      - 7151:7151
```
with `peer-base.yaml` defined below
```yaml
version: '2'

services:
    peer-base:
        image: hyperledger/fabric-peer:$IMAGE_TAG
        environment:
            # Generic peer variables
            - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
            - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_test
            - FABRIC_LOGGING_SPEC=WARN:cauthdsl=info:policies=debug:msp=info
            - CORE_PEER_TLS_ENABLED=true
            - CORE_PEER_GOSSIP_USELEADERELECTION=true
            - CORE_PEER_GOSSIP_ORGLEADER=false
            - CORE_PEER_PROFILE_ENABLED=true
            - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
            - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
            - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
        working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
        command: peer node start
        networks:
            - net_test # docker network for Test Network
```

That's basically it. Make sure that all certificates and keys are pointing in the correct and existing folder and you're good to go.

Enjoy your new network!