We have our Hyperledger Fabric Network bootstrapped now. There is a channel, peers have joined that channel, chaincode is installed and instantiated. As part of instantiation we provided the name of the entities and their initial values. We should be able to query
the values of an entity say the entity a
.
The steps to perform query of the data in the ledger are done in src/query-chaincode.ts script. Here is the relevant code snippet that shows the important steps:
async function queryChaincode(org: Organization) {
const client = await getClient(org);
const channel = await getChannel(client, org);
console.log(`Quering the Chaincode on the peers of ${org} ..`);
const response = await channel.queryByChaincode({
chaincodeId: config.CHAIN_CODE_ID,
fcn: 'query',
args: ["a"],
txId: client.newTransactionID()
});
console.log(`Peer0 of ${org} has ${response[0].toString('utf8')} as the current value for 'a'..`);
console.log(`Peer1 of ${org} has ${response[1].toString('utf8')} as the current value for 'a'..`);
}
async function main() {
console.log('############ ORG1 ###################');
await queryChaincode(Organization.ORG1);
console.log('############ ORG2 ###################');
await queryChaincode(Organization.ORG2);
console.log('############ ORG3 ###################');
await queryChaincode(Organization.ORG3);
}
main();
To run this script issue following command:
npm run query-chaincode
You should following output:
$ npm run query-chaincode
> hyperledger-fabric-example@0.1.0 query-chaincode /Users/ksachdeva/Desktop/Dev/myoss/hyperledger-fabric-example
> ts-node src/query-chaincode.ts
############ ORG1 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org1 ..
Peer0 of org1 has 100 as the current value for 'a'..
Peer1 of org1 has 100 as the current value for 'a'..
############ ORG2 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org2 ..
Peer0 of org2 has 100 as the current value for 'a'..
Peer1 of org2 has 100 as the current value for 'a'..
############ ORG3 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org3 ..
Peer0 of org3 has 100 as the current value for 'a'..
Peer1 of org3 has 100 as the current value for 'a'..
Above execution of src/query-chaincode.ts shows that the value of a
in the ledgers of all the peers of all participating organizations is 100
which is the value that we provided at the time of instantiation of the chaincode.
What we would like to do next is to exercise the business logic of ‘moving/transferring’ some value from entity a
to entity b
. Below is shown the src/invoke-transaction.ts script that shows the necessary steps to perform this operation.
async function invokeTransactionOnPeers(org: Organization) {
const client = await getClient(org);
const orderer = await getOrderer(client);
console.log('Creating a Channel object ..');
const channel = client.newChannel(config.CHANNEL_NAME);
console.log('Specifying the orderer to connect to ..');
channel.addOrderer(orderer);
console.log('Getting the peers ..');
const peers = await getPeers(client, org);
peers.map(p => channel.addPeer(p));
console.log('Initializing the channel ..');
await channel.initialize();
console.log('Sending the Invoke Proposal ..');
const proposalResponse = await channel.sendTransactionProposal({
chaincodeId: config.CHAIN_CODE_ID,
fcn: 'move',
args: ["a", "b", "10"],
txId: client.newTransactionID()
});
console.log('Sending the Transaction ..');
const transactionResponse = await channel.sendTransaction({
proposalResponses: proposalResponse[0],
proposal: proposalResponse[1]
});
}
async function main() {
await invokeTransactionOnPeers(Organization.ORG1);
}
main();
You can run the script by issuing command:
npm run invoke-transaction
Pay attention to the following part:
console.log('Sending the Invoke Proposal ..');
const proposalResponse = await channel.sendTransactionProposal({
chaincodeId: config.CHAIN_CODE_ID,
fcn: 'move',
args: ["a", "b", "10"],
txId: client.newTransactionID()
});
console.log('Sending the Transaction ..');
const transactionResponse = await channel.sendTransaction({
proposalResponses: proposalResponse[0],
proposal: proposalResponse[1]
});
move
.10
from a
to b
.sendTransactionProposal
is returning the proposal endorsed by the peers.sendTransaction
is what is finally making it commit to the various ledgers in the network.You should also note that unlike other scripts that we used in previous blog posts we are performing this transaction only using the client of one organization.
Time for the moment of truth -:
Even though we executed the transaction on the peers of organization 1, we are expecting that the ledgers of other peers of other organizations are updated as well i.e. the new value of entity a
on all the ledgers of all the peers should now be 90
(i.e. 100 - 10).
Run following command to query the chaincode:
npm run query-chaincode
and the output should look like
$ npm run query-chaincode
> hyperledger-fabric-example@0.1.0 query-chaincode /Users/ksachdeva/Desktop/Dev/myoss/hyperledger-fabric-example
> ts-node src/query-chaincode.ts
############ ORG1 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org1 ..
Peer0 of org1 has 90 as the current value for 'a'..
Peer1 of org1 has 90 as the current value for 'a'..
############ ORG2 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org2 ..
Peer0 of org2 has 90 as the current value for 'a'..
Peer1 of org2 has 90 as the current value for 'a'..
############ ORG3 ###################
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Creating a Channel object ..
Specifiying the orderer to connect to ..
Getting the peers ..
Initializing the channel ..
Quering the Chaincode on the peers of org3 ..
Peer0 of org3 has 90 as the current value for 'a'..
Peer1 of org3 has 90 as the current value for 'a'..
Here is a brief summary of what we have accomplished so far:
We now have a channel and peers of our participating organizations have joined, the next step is to provide the business logic that should be executed on this channel. In BlockChain this business logic is typically called Smart Contracts; Hyperledger Fabric call it Chaincode. A Chaincode in Fabric is essentially a program/application written in GoLang that exposes a set of interfaces that can be invoked by the client applications. The entry points of a Chaincode program/application also receives optional arguments and have access to the ledger so that it can read and write data to it.
The hyperledger-fabric-example project makes use of a very simple Chaincode application (chaincode/src/github.com/example_cc/example_cc.go
). The business logic is quite simple and described below:
If you look at the source code of (chaincode/src/github.com/example_cc/example_cc.go
) you should be able to see corresponding functions to the business logic requirements that I have specified above.
With the description of our business logic (Smart Contract / Chaincode) out of way, the first order of business (pun intended) is to install this application on the peers of various participating organizations.
The steps for performing the installation of Chaincode are done in src/install-chaincode.ts script and here is shown the relevant code snippet that should be descriptive enough for you to know how it is done :
async function installChaincodeOnPeers(org: Organization) {
const client = await getClient(org);
const orderer = await getOrderer(client);
console.log('Creating a Channel object ..');
const channel = client.newChannel(config.CHANNEL_NAME);
console.log('Specifying the orderer to connect to ..');
channel.addOrderer(orderer);
console.log('Getting the peers ..');
const peers = await getPeers(client, org);
// Note-
// The installChaincode is going to pick the chaincodePath
// from the local GOPATH
//
// Below I am just tricking it by setting the GOPATH environment
// variable and pointing it to the directory that contains the
// actual chain code
process.env.GOPATH = path.join(__dirname, '../chaincode');
const proposalResponse = await client.installChaincode({
targets: peers,
chaincodeId: config.CHAIN_CODE_ID,
chaincodePath: 'github.com/example_cc',
chaincodeVersion: 'v0'
});
}
async function main() {
await installChaincodeOnPeers(Organization.ORG1);
await installChaincodeOnPeers(Organization.ORG2);
await installChaincodeOnPeers(Organization.ORG3);
}
main();
You can run the script by issuing following command :
npm run install-chaincode
Once it is installed the next step is to instantiate (initialize) the Chaincode application. The steps for it are shown in src/instantiate-chaincode.ts script.
async function instantiateChaincodeOnPeers(org: Organization) {
const client = await getClient(org);
const orderer = await getOrderer(client);
console.log('Creating a Channel object ..');
const channel = client.newChannel(config.CHANNEL_NAME);
console.log('Specifying the orderer to connect to ..');
channel.addOrderer(orderer);
console.log('Getting the peers ..');
const peers = await getPeers(client, org);
peers.map(p => channel.addPeer(p));
console.log('Initializing the channel ..');
await channel.initialize();
console.log('Sending the Instantiate Proposal ..');
const proposalResponse = await channel.sendInstantiateProposal({
chaincodeId: config.CHAIN_CODE_ID,
chaincodeVersion: 'v0',
fcn: 'init',
args: ["a", "100", "b", "200"],
txId: client.newTransactionID()
});
console.log('Sending the Transaction ..');
const transactionResponse = await channel.sendTransaction({
proposalResponses: proposalResponse[0],
proposal: proposalResponse[1]
});
}
async function main() {
await instantiateChaincodeOnPeers(Organization.ORG1);
await instantiateChaincodeOnPeers(Organization.ORG2);
await instantiateChaincodeOnPeers(Organization.ORG3);
}
main();
You can run the script by issuing following command :
npm run instantiate-chaincode
Pay attention to the request object passed as argument of channel.sendInstantiateProposal
method.
const proposalResponse = await channel.sendInstantiateProposal({
chaincodeId: config.CHAIN_CODE_ID,
chaincodeVersion: 'v0',
fcn: 'init',
args: ["a", "100", "b", "200"],
txId: client.newTransactionID()
});
Here fcn
is the name of the function to be invoked. Look in example_cc.go
file and you would see a corresponding function that is named init
. The arguments that this function expects is an array of keyvalue pair with key being the name of the entity and the value being the value of the asset. So what we are passing are two entities that are named ‘a’ & ‘b’ with values of ‘100’ & ‘200’ respectively.
This marks the completion of bootstrapping a Hyperledger Fabric network with Business logic (Smart Contract / Chaincode) installed and instantiated.
Here is a brief summary of what we have accomplished so far:
We have created a channel and ordering service has a record of it as well but we need instruct the peers to join the channel and before we could do that we need to start the peer containers as well.
When you used
npm run start-orderer
it is starting the docker-compose configuration specified indocker-compose-orderer.yaml
file.
I have added a new docker-compose.yaml
file that specifies the peers to start and as well as inherit the configuration for orderer from docker-compose-orderer.yaml
file. So from now onwards we would not use the npm run start-orderer
command line. In order to start all the containers you would use following command:
# Deletes the production folder & Start all the containers (orderer, and peers of all organizations)
npm run start-containers
To stop all the containers use:
# Stop all the containers
npm run stop-containers
After successful issuance of npm run start-containers
you should be able to see following by issuing docker ps -a
command
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
53c08da6ccbc hyperledger/fabric-peer "peer node start" 9 seconds ago Up 6 seconds 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.ksachdeva-exp.com
e556c2066079 hyperledger/fabric-peer "peer node start" 9 seconds ago Up 6 seconds 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.ksachdeva-exp.com
7115ff0db9be hyperledger/fabric-peer "peer node start" 9 seconds ago Up 6 seconds 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.ksachdeva-exp.com
ecdb93e7e8d6 hyperledger/fabric-peer "peer node start" 9 seconds ago Up 5 seconds 0.0.0.0:12051->7051/tcp, 0.0.0.0:12053->7053/tcp peer1.org3.ksachdeva-exp.com
9c8b364b0b50 hyperledger/fabric-peer "peer node start" 9 seconds ago Up 7 seconds 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.ksachdeva-exp.com
509ce81e3748 hyperledger/fabric-orderer "orderer" 9 seconds ago Up 7 seconds 0.0.0.0:7050->7050/tcp orderer.ksachdeva-exp.com
45de91356f00 hyperledger/fabric-peer "peer node start" 9 seconds ago Up 7 seconds 0.0.0.0:11051->7051/tcp, 0.0.0.0:11053->7053/tcp peer0.org3.ksachdeva-exp.com
So now we have 2 peers per organization and 1 orderer node running in the docker containers.
In order to instruct the peers to join the channel here are the steps we are supposed to perform for every organization:
Client
object that has the user context set to the admin of the desired organization.Channel
object and the provide the instance of Orderer
to it.Channel
would obtain the genesis block from the ordering service.Peer
objects for the desired organization.Channel
objectAbove mentioned steps are demonstrated by the src/join-channel.ts script and here is the relevant code snippets that are descriptive enough to understand what is happening.
async function joinOrgPeersToChannel(org: Organization) {
const client = await getClient(org);
const orderer = await getOrderer(client);
console.log('Creating a Channel object ..');
const channel = client.newChannel(config.CHANNEL_NAME);
console.log('Specifying the orderer to connect to ..');
channel.addOrderer(orderer);
console.log('Getting the genesis block for the ${CHANNEL_NAME} ..');
const genesis_block = await channel.getGenesisBlock({
txId: client.newTransactionID()
});
console.log('Getting the peers ..');
const peers = await getPeers(client, org);
const proposalResponse = await channel.joinChannel({
txId: client.newTransactionID(),
block: genesis_block,
targets: peers
});
console.log(proposalResponse);
}
async function main() {
await joinOrgPeersToChannel(Organization.ORG1);
await joinOrgPeersToChannel(Organization.ORG2);
await joinOrgPeersToChannel(Organization.ORG3);
}
main();
Running the script src/join-channel.ts can be done by issuing following command:
npm run join-channel
Look in the production
folder and you should see various new folders & files that have been created. Majority of them are leveldb databases so the contents of the files would not make sense.
Here is a brief summary of what we have accomplished so far:
The configuration transaction block that we have created needs to be submitted to the Orderer nodes and it should be signed. The default policy in Fabric is that it should be signed by an admin user of at least one participating organization.
Hyperledger Fabric uses gRPC for the various services offered by the participating entities (peers & orderer) and Google Protobuf for the messages exchanged on the wire. However instead of directly using gRPC, the Fabric project provides SDKs in various languages (at present NodeJS, Java and Golang) that not only wraps the gRPC related aspect but also provide many other required utilities and a framework that client applications can make use to transact in the network and communicate with peers and orderers.
In this post we are going to use the NodeJS SDK to sign the configuration transaction block and submit it to the orderer. As with any other nodejs based project, the SDK is released in form of a library/module on npm but note that it targets ES6 (ECMAScript 2015) and at present can only be run using nodejs.
I have created a companion project (hyperledger-fabric-example) on github where I use fabric-client module to write the example scripts and explain the usage of its API and achieve the objectives of this post. This sample project is written using Typescript.
If you are not familiar with Typescript I would recommend reading an open source book on it by Basarat Syed - https://basarat.gitbooks.io/typescript/content/docs/getting-started.html
The classes exposed by NodeJS SDK are really well documented (https://fabric-sdk-node.github.io/) however at this point of time there is no type declaration file that helps in providing the intelli-sense and type safety so as part of this example project I have also started to write the corresponding typescript declarations and would submit them to DefinitelyTyped in near future.
The SDK exposes classes for every concept/entity that we have discussed so far (for e.g. Peer, Orderer, Channel) and interfaces for performing crypto operations, membership services, state store etc along with at least one default implementation for them. However, the main class that is exposed at the root of the module is a class called Client with helper methods to create objects of most of other classes discussed earlier. As an example, it is not recommended to create an object of Channel directly but use the factory method provided by the Client class.
There is another important class called User which represents the signing identity for the transactions sent to various entities in the network. Again, like other classes you typically use a factory method to create the user object.
Before we could sign the channel configuration transaction we need to perform 3 operations -
I have wrapped these steps in src/client.ts that exposes a method called getClient().
export async function getClient(org: Organization): Promise<Client> {
const client = new Client();
console.log('Setting up the cryptoSuite ..');
// ## Setup the cryptosuite (we are using the built in default s/w based implementation)
const cryptoSuite = Client.newCryptoSuite();
cryptoSuite.setCryptoKeyStore(Client.newCryptoKeyStore({
path: `${KEY_STORE_PATH_ADMIN}-${org}`
}));
client.setCryptoSuite(cryptoSuite);
console.log('Setting up the keyvalue store ..');
// ## Setup the default keyvalue store where the state will be stored
const store = await Client.newDefaultKeyValueStore({
path: `${KEY_STORE_PATH_ADMIN}-${org}`
});
client.setStateStore(store);
console.log('Creating the admin user context ..');
const ORG_ADMIN_MSP = MSP_DIR[org];
const privateKeyFile = fs.readdirSync(__dirname + '/../' + ORG_ADMIN_MSP + '/keystore')[0];
// ### GET THE NECESSRY KEY MATERIAL FOR THE ADMIN OF THE SPECIFIED ORG ##
const cryptoContentOrgAdmin: IIdentityFiles = {
privateKey: ORG_ADMIN_MSP + '/keystore/' + privateKeyFile,
signedCert: ORG_ADMIN_MSP + '/signcerts/Admin@' + org + '.ksachdeva-exp.com-cert.pem'
};
await client.createUser({
username: `${org}-admin`,
mspid: MSP_ID[org],
cryptoContent: cryptoContentOrgAdmin
});
return client;
}
The function and comments in it should be sufficient but here are some important points to note:
Since the channel creation request is to be sent to the Orderer we also need an object for Orderer that contains the necessary configuration (e.g. URL to connect to). Again as mentioned earlier instead of directly instantiating it we would use the factory method provided by the Client class.
export async function getOrderer(client: Client): Promise<Orderer> {
// build an orderer that will be used to connect to it
const data = fs.readFileSync(path.join(__dirname, ORDERER_TLS_CAROOT_PATH));
const orderer: Orderer = client.newOrderer(ORDERER_URL, {
'pem': Buffer.from(data).toString(),
'ssl-target-name-override': 'orderer.ksachdeva-exp.com'
});
return orderer;
}
Now that we have objects of Client and Orderer, it is time to extract the channel configuration from ksachdeva-exp-channel-1.tx, sign it, construct the channel creation request and send it to the Orderer. All these steps are shown in src/create-channel.ts script.
const CHANNEL_NAME = 'ksachdeva-exp-channel-1';
const CHANNEL_1_PATH = './../ksachdeva-exp-channel-1.tx';
async function main() {
const org1Client = await getClient(Organization.ORG1);
const orderer = await getOrderer(org1Client);
// read in the envelope for the channel config raw bytes
console.log('Reading the envelope from manually created channel transaction ..');
const envelope = fs.readFileSync(path.join(__dirname, CHANNEL_1_PATH));
// extract the configuration
console.log('Extracting the channel configuration ..');
const channelConfig = org1Client.extractChannelConfig(envelope);
console.log('Signing the extracted channel configuration ..');
const signature = org1Client.signChannelConfig(channelConfig);
// prepare the request
const channelRequest: IChannelRequest = {
name: CHANNEL_NAME,
config: channelConfig,
signatures: [signature],
orderer: orderer,
txId: org1Client.newTransactionID()
};
console.log('Sending the request to create the channel ..');
const response = await org1Client.createChannel(channelRequest);
console.log(response);
}
Make sure that the docker container for orderer is running. If not you can use the npm script to start it
# This is essentially calling the docker-compose. See package.json and its scripts section
npm run start-orderer
You can then run src/create-channel.ts by using npm as follows:
# This is essentially calling the ts-node. See package.json and its scripts section
npm run create-channel
A successful execution of the script should result in following output
$ npm run create-channel
> hyperledger-fabric-example@0.1.0 create-channel /Users/ksachdeva/Desktop/Dev/exp/hyperledger/hyperledger-fabric-example
> ts-node src/create-channel
Setting up the cryptoSuite ..
Setting up the keyvalue store ..
Creating the admin user context ..
Reading the envelope from manually created channel transaction ..
Extracting the channel configuration ..
Signing the extracted channel configuration ..
Sending the request to create the channel ..
status: 'SUCCESS'
Congratulations! we successfully created the channel. I have mapped the folder in the docker container for orderer that contains the ledger for channel configuration to the local file system. You should see a folder called ‘production’ and it sub contents as shown in the image below.
The production/orderer/chains/ksachdeva-exp-channel-1 is the new channel that we created. The production/orderer/chains/testchainid is the system channel that got created when we created the genesis block for orderer.
As you can guess that if you will run the src/create-channel.ts script again it should result in the failure. If you want to run the script few more times then you would want to issue following commands first
# Stop the orderer container(s)
npm run stop-orderer
# Start the orderer containers (s)
npm run start-orderer
In the npm run start-orderer script, I am first deleting the production folder so that the orderer container starts cleanly i.e. with out ksachdeva-exp-channel-1 channel.
So far we have only started an orderer node that is aware of identities of various participants in the network. Before we go further and start the other nodes in the network we need to talk about the most important concept in Hyperledger Fabric i.e Channels.
If you are familiar with pub-sub concept in a typical message queue implementation then you can consider that a channel is akin to ‘topic’. In other words, it is not a transport layer but a way to logical isolate the communication between various nodes. Entities that are the members of a channel may transact on it and more importantly the transactions on a channel are not visible on other channels. This ensures both authorization and the privacy aspects of transactions being committed on the network.
The Hyperledger Fabric wiki has a pretty good description of the channels.
Important things to note/consider are -
So how do we create a channel in the first place ? And quite possibly there will be more than one channel in a given network and it is also possible that more participants will join an existing channel in future. Given all this we can see that there is a requirement to create/update/read/delete the configuration related to the channels i.e. a database (or a ledger). This ledger resides with the orderer nodes of the network.
We should also note that a participating entity/user should have the permission to create a new channel. How to assign the permissions (or define the policies around) is a different blog post that I would do in near future. For now you should know that the default policy allows the Admin users to create the new channels. In Part 1 we had created the necessary cryptographic key material and in Part 2 we had provided the information about this aspect as part of genesis block to the orderer nodes.
Since the configuration of channels is stored in form of a ledger we can easily see that we should be creating a transaction and send it to the orderer nodes. At present the creation of this transaction is done using the same tool that we used to create the genesis block for orderer nodes i.e. configtxgen. We use a different profile specified in the configtx.yaml that specify the channel and its participants (all three organizations intend to be on this channel).
configtxgen -profile ThreeOrgsChannel -outputCreateChannelTx ./ksachdeva-exp-channel-1.tx -channelID ksachdeva-exp-channel-1
The file ksachdeva-exp-channel-1.tx contains the transaction block however it is not signed. Before we can send the request to the orderer node we need to sign this request using the cryptographic material of admin user of one of the participating organizations.
In the Part 1 I talked about the various entities in the Hyperledger Fabric Network and ended up creating certificates & private keys for them using cryptogen tool.
In this post I will try to run the ordering service (consists of only 1 node). Hyperledger Fabric project provides docker images that can be easily customized by either using them as base image and/or by providing the environment variables and volumes to provide the input data.
Because of the heavy usage of docker tools (specially Docker Compose) I would strongly suggest that you first acquaint yourself with it. One of the best resources other than the documentation provided by Docker is an open source tutorial called docker-curriculum.
Here is a list of items we need to start the Ordering service:
All of the above information except the private keys for the Orderers is captured in genesis block. Hyperledger Fabric project provides a tool called configtxgen (that also got downloaded by the script that we executed in Part 1) and very much like cryptogen this tool requires a yaml input file so as to generate the genesis block.
The Hyperledger Fabric wiki provides a good information on what is expected in the yaml file expected by configtxgen tool. Here is the one that we would use:
Things for you to note are MSP directories specified in the yaml file. If you will go inside these directories (note - crypto-config directory was created by cryptogen tool in the Part 1) to see that they contain only public certificates and no secret key material.
Time to generate the genesis block
configtxgen -profile ThreeOrgsOrdererGenesis -outputBlock ./genesis.block
The genesis.block is a binary file. If you want to inspect the contents of it you can issue following command :
configtxgen -profile ThreeOrgsOrdererGenesis -inspectBlock ./genesis.block
Now we have all the pieces that are required to start the orderer service. We will use docker compose to specify the various environment variables and mapping of artifacts (certs, keys, genesis block etc) that we have generated using the volumes. Here is the file :
In order to execute it use following command:
docker-compose -f docker-compose-orderer.yaml up
If you inspect the running containers (docker ps -a) you should now see a container named orderer.ksachdeva-exp.com. Also look at the logs that are generated when you started the orderer service.
If you want to run the orderer service in the background then use following command -
docker-compose -f docker-compose-orderer.yaml up -d