Hyperledger Fabric Part 1

Intro to Hyperledger Fabric via the Docs

Updated: 03 September 2023

Resources

[Prerequisites]((https://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html)

Before you can really get started you will need to first install the necessary prerequisites

  • Curl
  • Docker and Docker Compose
  • GoLang
  • Node and NPM
  • Python 2.7

Installing Samples

Select a directory in which the fabric-samples should be downloaded, for simplicity use your ~ directory

Terminal window
1
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh | bash -s 1.3.0

If you run ito the following error while running the above command

1
docker: Got permission denied while trying to connect to the Docker daemon socket at unix

Run the following command, then reboot your machine and try to run the previous command again

Terminal window
1
sudo usermod -a -G docker $USER

Next add the path to the downloaded files to your environment with

Terminal window
1
export PATH=~/fabric-samples/bin:$PATH

Writing your First Application

Setting up the environment

Once the necessary prerequisites have been installed and the fabric-samples folder has been downloaded navigate into the fabcar directory within it and ls

Terminal window
1
cd fabric-samples/fabcar
2
ls

Note the startFabric.sh file, as this will be needed in future

In this folder you should see a few choices for javascript/typescript, navigate in to the javascript directory

Terminal window
1
cd javascript
2
ls

This should then contain the following files

1
enrollAdmin.js invoke.js package.json query.js registerUser.js wallet

Next, clear any active docker containers

Terminal window
1
docker rm -f $(docker ps -aq)
2
docker network prune

If this tutorial has been done before, also remove the chaincode image with the following

Terminal window
1
docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba

Installing Clients and Launching the Network

From the fabcar/javascript directory install the dependencies and start fabric from the fabcar folder

Terminal window
1
npm install
2
cd ..
3
./startFabric.sh

Enroll the Admin User

Open a new terminal and run the following command to stream Docker logs

Terminal window
1
docker logs -f ca.example.com

When the network was launched, an admin user was registered as the Certificate Authority, the admin object will then be used to register and enroll new users

We now need to send an enrollment call and retrieve the Enrollment Certificate for the admin user. Switch back to the fabcar/javascript directory and run the following

Terminal window
1
node enrollAdmin.js

If this works you should see the following output

1
Successfully enrolled admin user "admin" and imported it into the wallet

Enroll a new User

We can now register a new user using the admin eCert to communicate with the CA server. This user1 identity will be used when querying and updating the ledger

Still in the fabcar/javascript directory, run the following

Terminal window
1
node registerUser.js

This will yield the following output if it works

1
Successfully registered and enrolled admin user "user1" and imported it into the wallet

Querying the Ledger

Queries are how you read data from the ledger, data is stored as key-value pairs and we can query for the value of a single key or multiple keys, if the ledger is written in a format like JSON we can perform more complex search operations

We can query the ledger to return all cars on it with the user1 identity. The query.js file contains the following line that specifies the signer

1
fabric_client.getUserContext('user1', true)

To run the query, from the fabcar/javascript folder, run the following command

Terminal window
1
node query.js

Which should return something like this

Terminal window
1
Successfully loaded user1 from persistence
2
Query has completed, checking results
3
Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
4
{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
5
{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
6
{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
7
{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
8
{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
9
{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
10
{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
11
{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
12
{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]

The query in the query.js file is constructed with the following code

1
// Create a new gateway for connecting to our peer node.
2
const gateway = new Gateway()
3
await gateway.connect(ccp, {
4
wallet,
5
identity: 'user1',
6
discovery: { enabled: false },
7
})
8
9
// Get the network (channel) our contract is deployed to.
10
const network = await gateway.getNetwork('mychannel')
11
12
// Get the contract from the network.
13
const contract = network.getContract('fabcar')
14
15
// Evaluate the specified transaction.
16
// queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4')
17
// queryAllCars transaction - requires no arguments, ex: ('queryAllCars')
18
const result = await contract.evaluateTransaction('queryAllCars')

When the query was run, it invoked the fabcar chaincode on the peer and ran the queryAllCars function within it, we can lok at the fabric-samples/chaincode/fabcar/javascript/lib/fabcar.js file to see the function that was evoked, which is the following

1
async queryAllCars(ctx) {
2
const startKey = 'CAR0';
3
const endKey = 'CAR999';
4
5
const iterator = await ctx.stub.getStateByRange(startKey, endKey);
6
7
const allResults = [];
8
while (true) {
9
const res = await iterator.next();
10
11
if (res.value && res.value.value.toString()) {
12
console.log(res.value.value.toString('utf8'));
13
14
const Key = res.value.key;
15
let Record;
16
try {
17
Record = JSON.parse(res.value.value.toString('utf8'));
18
} catch (err) {
19
console.log(err);
20
Record = res.value.value.toString('utf8');
21
}
22
allResults.push({ Key, Record });
23
}
24
if (res.done) {
25
console.log('end of data');
26
await iterator.close();
27
console.info(allResults);
28
return JSON.stringify(allResults);
29
}
30
}
31
}

The above pattern of using an application to interface with a smart contract which in turn interacts with the ledger is how transactions are done on the blockchain

If we want to modify our query to only search for CAR4 we can change the following line:

1
const result = await contract.evaluateTransaction('queryAllCars')

To be as follows:

1
fabric - samples / chaincode / fabcar / javascript / lib / fabcar.js

Running the query again from the terminal

1
node query.js

Should return the following

Terminal window
1
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}

Using the queryCar function we can query any cat in the ledger

Updating the Ledger

The invoke.js file will update the ledger by creating a car. The application will propose an update, and receive the endorsed update which it will then send to be written to every peer’s ledger

The request in the invoke.js file can be seen below

1
const gateway = new Gateway()
2
await gateway.connect(ccp, {
3
wallet,
4
identity: 'user1',
5
discovery: { enabled: false },
6
})
7
8
// Get the network (channel) our contract is deployed to.
9
const network = await gateway.getNetwork('mychannel')
10
11
// Get the contract from the network.
12
const contract = network.getContract('fabcar')
13
14
// Submit the specified transaction.
15
// createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom')
16
// changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave')
17
await contract.submitTransaction(
18
'createCar',
19
'CAR12',
20
'Honda',
21
'Accord',
22
'Black',
23
'Tom'
24
)
25
console.log('Transaction has been submitted')
26
27
// Disconnect from the gateway.
28
await gateway.disconnect()

We can run the transaction with

Terminal window
1
node transaction.js

Which will result in the following ouput

Terminal window
1
Transaction has been submitted

We can now modify the transaction to update CAR10 by changing

1
await contract.submitTransaction(
2
'createCar',
3
'CAR12',
4
'Honda',
5
'Accord',
6
'Black',
7
'Tom'
8
)

To

1
await contract.submitTransaction('changeCarOwner', 'CAR10', 'Dave')

And run invoke.js again

Terminal window
1
node query.js

If we run query.js for CAR10 this time, we should see the following

Terminal window
1
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"}

Cleanup

Clear any active docker containers

Terminal window
1
docker rm -f $(docker ps -aq)
2
docker network prune

If this tutorial has been done before, also remove the chaincode image with the following

Terminal window
1
docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba

Building your First Network

This tutorial needs to be run from the fabric-samples/first-network directory

Terminal window
1
cd fabric-samples/first-network

Network Builder Script

We can look at the help information for the byfn.sh script as follows

Terminal window
1
./byfn.sh --help
Terminal window
1
Usage:
2
byfn.sh <mode> [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-l <language>] [-i <imagetag>] [-v]
3
<mode> - one of 'up', 'down', 'restart', 'generate' or 'upgrade'
4
- 'up' - bring up the network with docker-compose up
5
- 'down' - clear the network with docker-compose down
6
- 'restart' - restart the network
7
- 'generate' - generate required certificates and genesis block
8
- 'upgrade' - upgrade the network from v1.0.x to v1.1
9
-c <channel name> - channel name to use (defaults to "mychannel")
10
-t <timeout> - CLI timeout duration in seconds (defaults to 10)
11
-d <delay> - delay duration in seconds (defaults to 3)
12
-f <docker-compose-file> - specify which docker-compose file use (defaults to docker-compose-cli.yaml)
13
-s <dbtype> - the database backend to use: goleveldb (default) or couchdb
14
-l <language> - the chaincode language: golang (default), node or java
15
-i <imagetag> - the tag to be used to launch the network (defaults to "latest")
16
-v - verbose mode
17
byfn.sh -h (print this message)
18
19
Typically, one would first generate the required certificates and
20
genesis block, then bring up the network. e.g.:
21
22
byfn.sh generate -c mychannel
23
byfn.sh up -c mychannel -s couchdb
24
byfn.sh up -c mychannel -s couchdb -i 1.1.0-alpha
25
byfn.sh up -l node
26
byfn.sh down -c mychannel
27
byfn.sh upgrade -c mychannel
28
29
Taking all defaults:
30
byfn.sh generate
31
byfn.sh up
32
byfn.sh down

The default channel name will be mychannel, the default timeout will be 10s

Generate Network Artifacts

To generate network artifacts we can run the following command

Terminal window
1
./byfn.sh generate

Which will have a description of what it will do and an option to continue

The first step generates all of the certificates and keys for our network entities, the genesis block used to bootstrap the ordering service, and a collection of configuration transactions required to configure the Channel

Bring Up the Network

You can bring up the network with the ./byfn.sh up command, which will by default use GoLang for the chaincode. If we want to use Node (which I do), use the following command instead

To bring up the network with Node as the Language, use the following command:

Terminal window
1
./byfn.sh up -l node

Furthermore if we want to use by defining a language channel name, and DB type, we can use look at the documentation for more commands on the script. For example if we wanted to use Node, and CouchDB with our channel called mychannel we can do that with the following

Terminal window
1
./byfn.sh up -c mychannel -s couchdb -l node

When the network is up and transacting you will see the following

1
Starting with channel 'mychannel' and CLI timeout of '10'
2
Continue? [Y/n]
3
proceeding ...
4
Creating network "net_byfn" with the default driver
5
Creating peer0.org1.example.com
6
Creating peer1.org1.example.com
7
Creating peer0.org2.example.com
8
Creating orderer.example.com
9
Creating peer1.org2.example.com
10
Creating cli
11
12
13
____ _____ _ ____ _____
14
/ ___| |_ _| / \ | _ \ |_ _|
15
\___ \ | | / _ \ | |_) | | |
16
___) | | | / ___ \ | _ < | |
17
|____/ |_| /_/ \_\ |_| \_\ |_|
18
19
Channel name : mychannel
20
Creating channel...

The above command will launch all the containers and run through a complete end-to-end application scenario, scrolling through these logs will allow you to see al the transactions

When complete you will see the following output

1
Query Result: 90
2
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
3
===================== Query successful on peer1.org2 on channel 'mychannel' =====================
4
5
===================== All GOOD, BYFN execution completed =====================
6
7
8
_____ _ _ ____
9
| ____| | \ | | | _ \
10
| _| | \| | | | | |
11
| |___ | |\ | | |_| |
12
|_____| |_| \_| |____/

If we want to use a different language we need to bring down and restart the network

Bringing Down the Network

You can bring down the network with the following command

Terminal window
1
./byfn.sh down

Crypto Generator

We use the cryptogen tool to generate cryptographic material for network entities. These certificates are representative of identities and allow for sign/verify authentication between entities

Cryptogen consumes a file crypto-config.yaml which contains the network topology and allows us to generate certificates and keys for Organizations and the Certificates belinging to them

Each organization is provisioned a unique ca-cert that binds certain peers and orderers to it. By assigning each Org a unique CA we mimic the way a typical network would work

Transactions are signed by a private key (keystore) and verified with a private key (signcerts)

The crypto-config.yaml file contains the following

1
# Copyright IBM Corp. All Rights Reserved.
2
#
3
# SPDX-License-Identifier: Apache-2.0
4
#
5
6
# ---------------------------------------------------------------------------
7
# "OrdererOrgs" - Definition of organizations managing orderer nodes
8
# ---------------------------------------------------------------------------
9
OrdererOrgs:
10
# ---------------------------------------------------------------------------
11
# Orderer
12
# ---------------------------------------------------------------------------
13
- Name: Orderer
14
Domain: example.com
15
# ---------------------------------------------------------------------------
16
# "Specs" - See PeerOrgs below for complete description
17
# ---------------------------------------------------------------------------
18
Specs:
19
- Hostname: orderer
20
# ---------------------------------------------------------------------------
21
# "PeerOrgs" - Definition of organizations managing peer nodes
22
# ---------------------------------------------------------------------------
23
PeerOrgs:
24
# ---------------------------------------------------------------------------
25
# Org1
26
# ---------------------------------------------------------------------------
27
- Name: Org1
28
Domain: org1.example.com
29
# ---------------------------------------------------------------------------
30
# "Specs"
31
# ---------------------------------------------------------------------------
32
# Uncomment this section to enable the explicit definition of hosts in your
33
# configuration. Most users will want to use Template, below
34
#
35
# Specs is an array of Spec entries. Each Spec entry consists of two fields:
36
# - Hostname: (Required) The desired hostname, sans the domain.
37
# - CommonName: (Optional) Specifies the template or explicit override for
38
# the CN. By default, this is the template:
39
#
40
# "{{.Hostname}}.{{.Domain}}"
41
#
42
# which obtains its values from the Spec.Hostname and
43
# Org.Domain, respectively.
44
# ---------------------------------------------------------------------------
45
# Specs:
46
# - Hostname: foo # implicitly "foo.org1.example.com"
47
# CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above
48
# - Hostname: bar
49
# - Hostname: baz
50
# ---------------------------------------------------------------------------
51
# "Template"
52
# ---------------------------------------------------------------------------
53
# Allows for the definition of 1 or more hosts that are created sequentially
54
# from a template. By default, this looks like "peer%d" from 0 to Count-1.
55
# You may override the number of nodes (Count), the starting index (Start)
56
# or the template used to construct the name (Hostname).
57
#
58
# Note: Template and Specs are not mutually exclusive. You may define both
59
# sections and the aggregate nodes will be created for you. Take care with
60
# name collisions
61
# ---------------------------------------------------------------------------
62
Template:
63
Count: 1
64
# Start: 5
65
# Hostname: {{.Prefix}}{{.Index}} # default
66
# ---------------------------------------------------------------------------
67
# "Users"
68
# ---------------------------------------------------------------------------
69
# Count: The number of user accounts _in addition_ to Admin
70
# ---------------------------------------------------------------------------
71
Users:
72
Count: 1

The naming convention for a network entity is <HOSTNAME>.<DOMAIN>, so for the ordering node we have orderer.example.com

After running the cryptogen tool, the generated certificates and keys will be saved to a folder called crypto-config

Configuration Transaction Generator

The configtxgen tool is used to generate four configuration artifacts

  • Orderer genisis block
  • Channel configuation transaction
  • Two anchor peer transactions - One for each Peer Org

The genesis block is the configuration block that initializes the ordering service and serves as the first block on a chain

Configtxgen works by consuming a configtx.yaml file which contains definitions for the network

In the configtx.yaml file we define the following

  • One Orderer Org OrdererOrg
  • Two Peer Orgs Org1 and Org2
  • A Consortium SampleConsortium

The file also has two unique headers

  • One for the Orderer Genesis Block TwoOrgsOrdererGenesis
  • One for the Channel TwoOrgsChannel

Which can be seen in the file below

1
# Copyright IBM Corp. All Rights Reserved.
2
#
3
# SPDX-License-Identifier: Apache-2.0
4
#
5
6
---
7
################################################################################
8
#
9
# Section: Organizations
10
#
11
# - This section defines the different organizational identities which will
12
# be referenced later in the configuration.
13
#
14
################################################################################
15
Organizations:
16
# SampleOrg defines an MSP using the sampleconfig. It should never be used
17
# in production but may be used as a template for other definitions
18
- &OrdererOrg
19
# DefaultOrg defines the organization which is used in the sampleconfig
20
# of the fabric.git development environment
21
Name: OrdererOrg
22
23
# ID to load the MSP definition as
24
ID: OrdererMSP
25
26
# MSPDir is the filesystem path which contains the MSP configuration
27
MSPDir: crypto-config/ordererOrganizations/example.com/msp
28
29
- &Org1
30
# DefaultOrg defines the organization which is used in the sampleconfig
31
# of the fabric.git development environment
32
Name: Org1MSP
33
34
# ID to load the MSP definition as
35
ID: Org1MSP
36
37
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
38
39
AnchorPeers:
40
# AnchorPeers defines the location of peers which can be used
41
# for cross org gossip communication. Note, this value is only
42
# encoded in the genesis block in the Application section context
43
- Host: peer0.org1.example.com
44
Port: 7051
45
46
################################################################################
47
#
48
# SECTION: Application
49
#
50
# - This section defines the values to encode into a config transaction or
51
# genesis block for application related parameters
52
#
53
################################################################################
54
Application: &ApplicationDefaults
55
# Organizations is the list of orgs which are defined as participants on
56
# the application side of the network
57
Organizations:
58
59
################################################################################
60
#
61
# SECTION: Orderer
62
#
63
# - This section defines the values to encode into a config transaction or
64
# genesis block for orderer related parameters
65
#
66
################################################################################
67
Orderer: &OrdererDefaults
68
# Orderer Type: The orderer implementation to start
69
# Available types are "solo" and "kafka"
70
OrdererType: solo
71
72
Addresses:
73
- orderer.example.com:7050
74
75
# Batch Timeout: The amount of time to wait before creating a batch
76
BatchTimeout: 2s
77
78
# Batch Size: Controls the number of messages batched into a block
79
BatchSize:
80
# Max Message Count: The maximum number of messages to permit in a batch
81
MaxMessageCount: 10
82
83
# Absolute Max Bytes: The absolute maximum number of bytes allowed for
84
# the serialized messages in a batch.
85
AbsoluteMaxBytes: 99 MB
86
87
# Preferred Max Bytes: The preferred maximum number of bytes allowed for
88
# the serialized messages in a batch. A message larger than the preferred
89
# max bytes will result in a batch larger than preferred max bytes.
90
PreferredMaxBytes: 512 KB
91
92
Kafka:
93
# Brokers: A list of Kafka brokers to which the orderer connects
94
# NOTE: Use IP:port notation
95
Brokers:
96
- 127.0.0.1:9092
97
98
# Organizations is the list of orgs which are defined as participants on
99
# the orderer side of the network
100
Organizations:
101
102
################################################################################
103
#
104
# Profile
105
#
106
# - Different configuration profiles may be encoded here to be specified
107
# as parameters to the configtxgen tool
108
#
109
################################################################################
110
Profiles:
111
OneOrgOrdererGenesis:
112
Orderer:
113
<<: *OrdererDefaults
114
Organizations:
115
- *OrdererOrg
116
Consortiums:
117
SampleConsortium:
118
Organizations:
119
- *Org1
120
OneOrgChannel:
121
Consortium: SampleConsortium
122
Application:
123
<<: *ApplicationDefaults
124
Organizations:
125
- *Org1

Run the Tools

We can make use of the configtxgen and cryptogen commands to do what we need, alternatively we can also adapt the byfn.sh script’s generateCerts function to meet our requirements

Before running the remainder of the tools, make sure the previous network is down by running the following

Terminal window
1
./byfn.sh down

If you run into an error that says cannot remove .... Permission denied run the command as sudo

Manually Generate the Artifacts

We can refer to the generateCerts function to see how we would go about doing this, but we can also do this using the binaries manually as follows

Terminal window
1
../bin/cryptogen generate --config=./crypto-config.yaml

Which should have the following output

1
org1.example.com
2
org2.example.com

Which will output the certs and keys into a cypto-config directory

Next we need to use the cofigtxgen tool as follows

Terminal window
1
export FABRIC_CFG_PATH=$PWD
2
3
../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

Which should have an output like

1
2017-10-26 19:21:56.301 EDT [common/tools/configtxgen] main -> INFO 001 Loading configuration
2
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 002 Generating genesis block
3
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 003 Writing genesis block

Create Channel Configuration

Create the CHANNEL_NAME environment variable

Terminal window
1
export CHANNEL_NAME=mychannel

And create the Channel Transaction Artifact

Terminal window
1
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

Then define the Anchor Peer for Org1 and Org2 as follows

Terminal window
1
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
2
3
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

Start the Network

We make use of the docker compose files to bring up the fabric containers and bootstrap the Orderer with the genesis.block

There are a few different steps as follows

Start the network from your terminal with the following command, the -d flag disables container logs, if you want to view the logs, run the following command in a new terminal

Terminal window
1
docker-compose -f docker-compose-cli.yaml up -d

Environment Variables

We need to configure some environment variables. The variables for peer0.org1.example.com are coded into the CLI container via the docker-compose-cli.yaml file, however if we want to send calls to other peers or Orderers, we need to modify the following values in the cli.environment object in the yaml file before starting the network

1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
2
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
3
CORE_PEER_LOCALMSPID="Org1MSP"
4
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

Create and Join a Channel

We created the channel configuraion transaction using the configtxgen tool, that process can be repeated to create additional channel configurations with the configtx.yaml file by using the same or different profiles

We enter the CLI container next with docker exec

Terminal window
1
docker exec -it cli bash

If successful you will see the following

Terminal window
1
root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#

Now we need to set previously mentioned variables in the cli container as they are different to what we had previously set in the docker-compose-cli.yaml file. From the Container’s terminal run the following

Terminal window
1
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
2
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
3
export CORE_PEER_LOCALMSPID="Org1MSP"
4
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

Next we specify the channel name with the -c flag, and the channel transaction with the -f flag, we will create the channel with the following from our terminal:

Terminal window
1
export CHANNEL_NAME=mychannel
2
3
# the channel.tx file is mounted in the channel-artifacts directory within your CLI container
4
# as a result, we pass the full path for the file
5
# we also pass the path for the orderer ca-cert in order to verify the TLS handshake
6
# be sure to export or replace the $CHANNEL_NAME variable appropriately
7
8
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

If you run into an error at this stage, such as the one below

Terminal window
1
Error: error getting endorser client chaincode: endorser client failed to connect to peer0.org1.example.com:7051 failed to create new connection context deadline exceeded.

It likely indicatesthat the previous network was not taken down correctly, run sudo ./byfn. down and ensure that you have no running containers with docker container ls

Note the --cafile is the Certificate file for the Orderer allowing us to verify the TLS handshake

The command we just executed generated a proto called mychannel.block which we can see by running ls from within the container

We can now join peer0.org1.example.com to the channel we just created with the following

Terminal window
1
peer channel join -b mychannel.block

Thereafter join peer0.org2 by prefacing it with the appropriate environment variables and joining the channel on behalf of peer0.org2

Terminal window
1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

Update the Anchor Peers

Nexty we will perform channel updates which will propogate to the definition of the channel, essentially adding configuration deltas for the channel’s genesis block to define the anchor peers

We can define the anchor peer for Org1 as peer.0org1.example.com, as our environment variables still hold the values for

Terminal window
1
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

And for Org2 as peer0.org2.example.com by updating

Terminal window
1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Install and Instantiate Chaincode

Applications interact with the ledger through chaincode, we need to install chaincode on every peer that will need to execute and endorse out interactions. Chaincode can be written in Go, Java, and Javascript and runs on the peer in the context of a chaincode container with a specific name and version and exists on the peer’s filesystem

We can install chaincode on a peer with the following command, the -l flag allows us to specify the language

Terminal window
1
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

The -p argument is the path to the file and the -n is the name of the chaincode

Next we can instantiate the Chaincode on the peer, the -P flag indicates the endorsement policy needed, in the following case it is -P "AND ('Org1MSP.peer','Org2MSP.peer')"

Next, modify the environment variables and install the chaincode on peer 2

Terminal window
1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
2
CORE_PEER_ADDRESS=peer0.org2.example.com:7051
3
CORE_PEER_LOCALMSPID="Org2MSP"
4
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
Terminal window
1
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

We can instantiate the chaincode with the following

Terminal window
1
peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -l node -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

Note that the above command may take a while to execute for Node and Java as it is also installing a shim and container respectively

Verify Chaincode

We can make some queries and transactions to verify that the chaincode was correctly installed

Query

we can query the value od a to make sure the state DB was populated as follows

Terminal window
1
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

The queey should return 100

Invoke

Next, move 10 from a to b

Terminal window
1
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

Query

Thereafter, query the value again to verify that the transfer succeeded

Terminal window
1
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

We should now see 90

Install on New Peer

Now, Install the chaincode on a third peer peer1.org2, first set the folowing environment variables

Terminal window
1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
2
CORE_PEER_ADDRESS=peer1.org2.example.com:7051
3
CORE_PEER_LOCALMSPID="Org2MSP"
4
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt

Then install the chaincode

Terminal window
1
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

Join Channel

Next the new peer needs to join the channel before it can respond to queries

Terminal window
1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer1.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

Query

After a few seconds when the peer has joined the channel, we can submit the query

Terminal window
1
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

We should see the same output as before of 90

Take Down the Network

Lastly, we can take down the network with

Terminal window
1
./byfn.sh down

The process we just covered is the same as what happens when we run ./byfn.sh up, we can run this again and look at the logs in order to see the above with some neat output which I will not put here

Important Points

  • Chaincode must be installed on a peer for it to successfully read and write
  • Chaincode is not instantiated until an init or read/write transaction is performed against the chaincode
  • An intial transaction causes a container to start
  • All peers on a channel maintain the same copy of the ledger, even those without the chaincode installed on them

Viewing Transactions

We can see transactions by looking at the logs for the CLI container

Terminal window
1
docker logs -f cli

Furhtermore we can see the chaincode logs by looking at the logs for each respective peer

Terminal window
1
docker logs dev-peer0.org2.example.com-mycc-1.0
2
dokcer logs dev-peer0.org1.example.com-mycc-1.0
3
docker logs dev-peer1.org2.example.com-mycc-1.0

CouchDB

We can switch the database from GloveDB to CouchDB in order to allow us to make more complex queries additional to the standard chaincode functionality, to use CouchDB we simply compose with the docker-compose-couch.yaml file as follows

Terminal window
1
docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d

While running a development environment we can map CouchDB to a port in order to allow us to use the REST API and visualize the Db with the Web UI, however port mapping is not recommended for Production environments

If we want to look at a tutorial for doing the above with CouchDB we can find that here

CouchDB allows us to store more complex JSON data in a fully queryable format, as well as enhancing security for compliance and allows us to do field-level security such as calue masking and filtering

Troubleshooting

If you need to shoot some trouble you can find some information in the Fabric Docs