Tutorial Hyperledger Fabric SDK Go: How to build your first app?

This tutorial will introduce you to the Hyperledger Fabric Go SDK and allows you to build a simple application using the blockchain principle.

This tutorial uses Hyperledger Fabric version 1.1.0, on github Heroes-Service use the branch 1.1.0!

This is the first part of this tutorial. The basics SDK features will be shown, but the second part is scheduled to demonstrate a more complex application.

  1. Prerequisites
  2. Introduction to Hyperledger Fabric
  3. Installation guide
    1. Docker
    2. Docker Compose
    3. Go
  4. Make your first blockchain network
    1. Prepare environment
    2. Test
  5. Use the Fabric SDK Go
    1. Configuration
    2. Initialise
    3. Test
    4. Clean up and Makefile
    5. Install & instantiate the chaincode
    6. Query the chaincode
    7. Change the ledger state
  6. Make this in a web application
  7. Referencies

Hyperledger Fabric SDK Go logo

1. Prerequisites

This tutorial won’t explain in detail how Hyperledger Fabric works. I will just give some tips to understand the general behavior of the framework. If you want to get a full explanation of the tool, go to the official documentation there is a lot of work there that explains what kind of blockchain Hyperledger Fabric is.

This tutorial has been made on Ubuntu 16.04 but the Hyperledger Fabric framework is compatible with Mac OS X, Windows and other Linux distributions.

We will use the Go language to design our first application because the Hyperledger Fabric has been also built in Go and the Fabric SDK Go is really simple to use. In addition, the chaincode (smart contract) can be written in Go too. So the full-stack will be only in Go! Awesome right ? However, if you are go phobic, there are other SDK like for NodeJS, Java or Python but we won’t discuss about them here.

Hyperledger Fabric uses Docker to easily deploy a blockchain network. In addition, some components (peers) also deploys docker containers to separate data (channel). So make sure that your platform supports this kind of virtualization.

2. Introduction to Hyperledger Fabric

Hyperledger Fabric is a platform for distributed ledger solutions underpinned by a modular architecture delivering high degrees of confidentiality, resiliency, flexibility and scalability. It is designed to support pluggable implementations of different components and accommodate the complexity and intricacies that exist across the economic ecosystem.

See the full explaination from the official documentation, in the introduction part: Hyperledger Fabric Blockchain

Blockchain Concensus

3. Installation guide

This tutorial was made on Ubuntu 16.04, but there is help for Windows, Mac OS X and other Linux distributions users.

a. Docker

Docker version 17.03.0-ce or greater is required.

Linux (Ubuntu)

First of all, in order to install docker correctly we need to install its dependencies:

sudo apt install apt-transport-https ca-certificates curl software-properties-common

Once the dependencies are installed, we can install docker:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
sudo apt update && \
sudo apt install -y docker-ce

Now we need to manage the current user to avoid using administration rights (root) access when we will use the docker command. To do so, we need to add the current user to the docker group:

sudo groupadd docker ; \
sudo gpasswd -a ${USER} docker && \
sudo service docker restart

Do not mind if groupadd: group 'docker' already exists error pop up.

To apply the changes made, you need to logout/login. You can then check your version with:

docker -v

Screenshot end of the Docker installation

Mac OS X

Download and install the latest Docker.dmg package for Mac OS X available on the Docker website. This will install docker-compose as well, so you can skip the next step.

Linux (not Ubuntu)

 

See links below:

 

 

Windows

See instructions from the Docker website: docker.com/docker-for-windows

b. Docker Compose

Docker-compose version 1.8 or greater is required.

We are currently unable to manage easily multiple containers at once. To solve this issue, we need docker-compose.

Linux

The installation is pretty fast:

sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose

Apply these changes by logout/login and then check its version with:

docker-compose version

Screenshot end of the Docker Compose installation

Windows / Others

See instructions from the Docker-compose website: docs.docker.com/compose/install

c. Go

Go version 1.9.x or greater is required.

Linux

You can either follow instructions from golang.org or use these generics commands that will install Golang 1.9.2 and prepare your environment (generate your GOPATH) for Ubuntu:

wget https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz && \
sudo tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz && \
rm go1.9.2.linux-amd64.tar.gz && \
echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee -a /etc/profile && \
echo 'export GOPATH=$HOME/go' | tee -a $HOME/.bashrc && \
echo 'export PATH=$PATH:$GOROOT/bin:$GOPATH/bin' | tee -a $HOME/.bashrc && \
mkdir -p $HOME/go/{src,pkg,bin}

To make sure that the installation works, you can logout/login (again) and run:

go version

Screenshot end of the Go installation

Windows / Mac OS X / Others

See instructions from the Golang website: golang.org/install

4. Make your first blockchain network

a. Prepare environment

In order to make a blockchain network, we will use docker to build virtual computers that will handle different roles. In this tutorial we will stay as simple as possible. Hyperledger Fabric needs a lot of certificates to ensure encryption during the whole end to end process (TSL, authentications, signing blocks…). The creation of these files requires a little time and in order to go straight to the heart of the matter, we have already prepared all this for you in the folder fixtures in this repository.

Make a new directory in the src folder of your GOPATH, following our repository naming:

mkdir -p $GOPATH/src/github.com/chainHero/heroes-service && \
cd $GOPATH/src/github.com/chainHero/heroes-service

To get the fixtures folder, you can either follow this command line, which will install and use subversion to get the folder from this repository. Or download the zip file from Github and extract only the fixtures folder.

sudo apt install -y subversion && \
cd $GOPATH/src/github.com/chainHero/heroes-service && \
svn checkout https://github.com/chainHero/heroes-service/branches/v1.1.0/fixtures &&
rm -rf fixtures/.svn

Alternatively, if you want to know how to build this fixture folder and learn how to create the blockchain network, follow this quick tutorial on how to build your first network.

b. Test

In order to check if the network works, we will use docker-compose to start or stop all containers at the same time. Go inside the fixtures folder, and run:

cd $GOPATH/src/github.com/chainHero/heroes-service/fixtures && \
docker-compose up

You will see a lot of logs with different colors (for your information, red isn’t equal to errors).

Open a new terminal and run:

docker ps

Screenshot command Docker ps

You will see : two peers, the orderer and one CA containers. You have successfully made a new network ready to use with the SDK. To stop the network go back to the previous terminal, press Ctrl+C and wait that all containers are stopped. If you want to explore more deeper, follow our tutorial dedicated to the network part here or check out the official documentation about this: Building Your First Network

Tips: when the network is stopped, all containers used remain accessible. This is very useful to check logs for example. You can see them with docker ps -a. In order to clean up these containers, you need to delete them with docker rm $(docker ps -aq) or if you have used a docker-compose file, go where this file is and run docker-compose down.

Tips: you can run the docker-compose command in background to keep the prompt. To do so, use the parameter -d, like this: docker-compose up -d. To stop containers, run in the same folder where the docker-compose.yaml is, the command: docker-compose stop (or docker-compose down to clean up after all containers are stopped).

5. Use the Fabric SDK Go

a. Configuration

Our application needs a lot of parameters, especially the addresses of the Fabric’s components to communicate. We will put everything in a new configuration file, the Fabric SDK Go configuration and our custom parameters. For the moment, we will only try to make the Fabric SDK Go works with the default chaincode:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi config.yaml

name: "heroes-service-network"
#
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
#
version: 1.0.0

#
# The client section used by GO SDK.
#
client:

  # Which organization does this application instance belong to? The value must be the name of an org
  # defined under "organizations"
  organization: org1

  logging:
    level: info

  # Global configuration for peer, event service and orderer timeouts
  # if this this section is omitted, then default values will be used (same values as below)
#  peer:
#    timeout:
#      connection: 10s
#      response: 180s
#      discovery:
#        # Expiry period for discovery service greylist filter
#        # The channel client will greylist peers that are found to be offline
#        # to prevent re-selecting them in subsequent retries.
#        # This interval will define how long a peer is greylisted
#        greylistExpiry: 10s
#  eventService:
#    # Event service type (optional). If not specified then the type is automatically
#    # determined from channel capabilities.
#    type: (deliver|eventhub)
    # the below timeouts are commented out to use the default values that are found in
    # "pkg/fab/endpointconfig.go"
    # the client is free to override the default values by uncommenting and resetting
    # the values as they see fit in their config file
#    timeout:
#      connection: 15s
#      registrationResponse: 15s
#  orderer:
#    timeout:
#      connection: 15s
#      response: 15s
#  global:
#    timeout:
#      query: 180s
#      execute: 180s
#      resmgmt: 180s
#    cache:
#      connectionIdle: 30s
#      eventServiceIdle: 2m
#      channelConfig: 30m
#      channelMembership: 30s
#      discovery: 10s
#      selection: 10m

  # Root of the MSP directories with keys and certs.
  cryptoconfig:
    path: ${GOPATH}/src/github.com/chainHero/heroes-service/fixtures/crypto-config

  # Some SDKs support pluggable KV stores, the properties under "credentialStore"
  # are implementation specific
  credentialStore:
    path: /tmp/heroes-service-store

    # [Optional]. Specific to the CryptoSuite implementation used by GO SDK. Software-based implementations
    # requiring a key store. PKCS#11 based implementations does not.
    cryptoStore:
      path: /tmp/heroes-service-msp

   # BCCSP config for the client. Used by GO SDK.
  BCCSP:
    security:
     enabled: true
     default:
      provider: "SW"
     hashAlgorithm: "SHA2"
     softVerify: true
     level: 256

  tlsCerts:
    # [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
    systemCertPool: false

    # [Optional]. Client key and cert for TLS handshake with peers and orderers
    client:
      keyfile:
      certfile:

#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
#
channels:
  # name of the channel
  chainhero:
    # Required. list of orderers designated by the application to use for transactions on this
    # channel. This list can be a result of access control ("org1" can only access "ordererA"), or
    # operational decisions to share loads from applications among the orderers.  The values must
    # be "names" of orgs defined under "organizations/peers"
    # deprecated: not recommended, to override any orderer configuration items, entity matchers should be used.
    # orderers:
    #  - orderer.example.com

    # Required. list of peers from participating orgs
    peers:
      peer0.org1.hf.chainhero.io:
        # [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
        # have the chaincode installed. The app can also use this property to decide which peers
        # to send the chaincode install request. Default: true
        endorsingPeer: true

        # [Optional]. will this peer be sent query proposals? The peer must have the chaincode
        # installed. The app can also use this property to decide which peers to send the
        # chaincode install request. Default: true
        chaincodeQuery: true

        # [Optional]. will this peer be sent query proposals that do not require chaincodes, like
        # queryBlock(), queryTransaction(), etc. Default: true
        ledgerQuery: true

        # [Optional]. will this peer be the target of the SDK's listener registration? All peers can
        # produce events but the app typically only needs to connect to one to listen to events.
        # Default: true
        eventSource: true

      peer1.org1.hf.chainhero.io:

    policies:
      #[Optional] options for retrieving channel configuration blocks
      queryChannelConfig:
        #[Optional] min number of success responses (from targets/peers)
        minResponses: 1
        #[Optional] channel config will be retrieved for these number of random targets
        maxTargets: 1
        #[Optional] retry options for query config block
        retryOpts:
          #[Optional] number of retry attempts
          attempts: 5
          #[Optional] the back off interval for the first retry attempt
          initialBackoff: 500ms
          #[Optional] the maximum back off interval for any retry attempt
          maxBackoff: 5s
          #[Optional] he factor by which the initial back off period is exponentially incremented
          backoffFactor: 2.0


#
# list of participating organizations in this network
#
organizations:
  org1:
    mspid: org1.hf.chainhero.io
    cryptoPath: peerOrganizations/org1.hf.chainhero.io/users/{userName}@org1.hf.chainhero.io/msp
    peers:
      - peer0.org1.hf.chainhero.io
      - peer1.org1.hf.chainhero.io

    # [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
    # network. Typically certificates provisioning is done in a separate process outside of the
    # runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
    # dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
    # Fabric-CA servers.
    certificateAuthorities:
      - ca.org1.hf.chainhero.io

#
# List of orderers to send transaction and channel create/update requests to. For the time
# being only one orderer is needed. If more than one is defined, which one get used by the
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
#
orderers:
  orderer.hf.chainhero.io:
    url: localhost:7050

    # these are standard properties defined by the gRPC library
    # they will be passed in as-is to gRPC client constructor
    grpcOptions:
      ssl-target-name-override: orderer.hf.chainhero.io
      # These parameters should be set in coordination with the keepalive policy on the server,
      # as incompatible settings can result in closing of connection.
      # When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      # allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
      allow-insecure: false

    tlsCACerts:
      # Certificate location absolute path
      path: ${GOPATH}/src/github.com/chainHero/heroes-service/fixtures/crypto-config/ordererOrganizations/hf.chainhero.io/tlsca/tlsca.hf.chainhero.io-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
  peer0.org1.hf.chainhero.io:
    # this URL is used to send endorsement and query requests
    url: localhost:7051
    # eventUrl is only needed when using eventhub (default is delivery service)
    eventUrl: localhost:7053

    grpcOptions:
      ssl-target-name-override: peer0.org1.hf.chainhero.io
      # These parameters should be set in coordination with the keepalive policy on the server,
      # as incompatible settings can result in closing of connection.
      # When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      # allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
      allow-insecure: false

    tlsCACerts:
      # Certificate location absolute path
      path: ${GOPATH}/src/github.com/chainHero/heroes-service/fixtures/crypto-config/peerOrganizations/org1.hf.chainhero.io/tlsca/tlsca.org1.hf.chainhero.io-cert.pem

  peer1.org1.hf.chainhero.io:
    # this URL is used to send endorsement and query requests
    url: localhost:8051
    # eventUrl is only needed when using eventhub (default is delivery service)
    eventUrl: localhost:8053

    grpcOptions:
      ssl-target-name-override: peer1.org1.hf.chainhero.io
      # These parameters should be set in coordination with the keepalive policy on the server,
      # as incompatible settings can result in closing of connection.
      # When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      # allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
      allow-insecure: false

    tlsCACerts:
      # Certificate location absolute path
      path: ${GOPATH}/src/github.com/chainHero/heroes-service/fixtures/crypto-config/peerOrganizations/org1.hf.chainhero.io/tlsca/tlsca.org1.hf.chainhero.io-cert.pem

#
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
# certificate management to be done via REST APIs. Application may choose to use a standard
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
#
certificateAuthorities:
  ca.org1.hf.chainhero.io:
    url: http://localhost:7054
    # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
    # needed to enroll and invoke new users.
    httpOptions:
      verify: false
    registrar:
      enrollId: admin
      enrollSecret: adminpw
    # [Optional] The optional name of the CA.
    caName: ca.org1.hf.chainhero.io
    tlsCACerts:
      # Certificate location absolute path
      path: ${GOPATH}/src/github.com/chainHero/heroes-service/fixtures/crypto-config/peerOrganizations/org1.hf.chainhero.io/ca/ca.org1.hf.chainhero.io-cert.pem

entityMatchers:
  peer:
    - pattern: (\w*)peer0.org1.hf.chainhero.io(\w*)
      urlSubstitutionExp: localhost:7051
      eventUrlSubstitutionExp: localhost:7053
      sslTargetOverrideUrlSubstitutionExp: peer0.org1.hf.chainhero.io
      mappedHost: peer0.org1.hf.chainhero.io

    - pattern: (\w*)peer1.org1.hf.chainhero.io(\w*)
      urlSubstitutionExp: localhost:8051
      eventUrlSubstitutionExp: localhost:8053
      sslTargetOverrideUrlSubstitutionExp: peer1.org1.hf.chainhero.io
      mappedHost: peer1.org1.hf.chainhero.io

  orderer:
    - pattern: (\w*)orderer.hf.chainhero.io(\w*)
      urlSubstitutionExp: localhost:7050
      sslTargetOverrideUrlSubstitutionExp: orderer.hf.chainhero.io
      mappedHost: orderer.hf.chainhero.io

  certificateAuthorities:
    - pattern: (\w*)ca.org1.hf.chainhero.io(\w*)
      urlSubstitutionExp: http://localhost:7054
      mappedHost: ca.org1.hf.chainhero.io

The configuration file is also available here: config.yaml

b. Initialise

We add a new folder named blockchain that will contain the whole interface that communicate with the network. We will see the Fabric SDK Go only in this folder.

mkdir $GOPATH/src/github.com/chainHero/heroes-service/blockchain

Now, we add a new go file named setup.go:

vi $GOPATH/src/github.com/chainHero/heroes-service/blockchain/setup.go

package blockchain

import (
	"fmt"
	mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/pkg/errors"
)

// FabricSetup implementation
type FabricSetup struct {
	ConfigFile      string
	OrgID           string
	OrdererID	string
	ChannelID       string
	ChainCodeID     string
	initialized     bool
	ChannelConfig   string
	ChaincodeGoPath string
	ChaincodePath   string
	OrgAdmin        string
	OrgName         string
	UserName        string
	admin           *resmgmt.Client
	sdk             *fabsdk.FabricSDK
}

// Initialize reads the configuration file and sets up the client, chain and event hub
func (setup *FabricSetup) Initialize() error {

	// Add parameters for the initialization
	if setup.initialized {
		return errors.New("sdk already initialized")
	}

	// Initialize the SDK with the configuration file
	sdk, err := fabsdk.New(config.FromFile(setup.ConfigFile))
	if err != nil {
		return errors.WithMessage(err, "failed to create SDK")
	}
	setup.sdk = sdk
	fmt.Println("SDK created")

	// The resource management client is responsible for managing channels (create/update channel)
	resourceManagerClientContext := setup.sdk.Context(fabsdk.WithUser(setup.OrgAdmin), fabsdk.WithOrg(setup.OrgName))
	if err != nil {
		return errors.WithMessage(err, "failed to load Admin identity")
	}
	resMgmtClient, err := resmgmt.New(resourceManagerClientContext)
	if err != nil {
		return errors.WithMessage(err, "failed to create channel management client from Admin identity")
	}
	setup.admin = resMgmtClient
	fmt.Println("Ressource management client created")

	// The MSP client allow us to retrieve user information from their identity, like its signing identity which we will need to save the channel
	mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(setup.OrgName))
	if err != nil {
		return errors.WithMessage(err, "failed to create MSP client")
	}
	adminIdentity, err := mspClient.GetSigningIdentity(setup.OrgAdmin)
	if err != nil {
		return errors.WithMessage(err, "failed to get admin signing identity")
	}
	req := resmgmt.SaveChannelRequest{ChannelID: setup.ChannelID, ChannelConfigPath: setup.ChannelConfig, SigningIdentities: []msp.SigningIdentity{adminIdentity}}
	txID, err := setup.admin.SaveChannel(req, resmgmt.WithOrdererEndpoint(setup.OrdererID))
	if err != nil || txID.TransactionID == "" {
		return errors.WithMessage(err, "failed to save channel")
	}
	fmt.Println("Channel created")

	// Make admin user join the previously created channel
	if err = setup.admin.JoinChannel(setup.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(setup.OrdererID)); err != nil {
		return errors.WithMessage(err, "failed to make admin join channel")
	}
	fmt.Println("Channel joined")

	fmt.Println("Initialization Successful")
	setup.initialized = true
	return nil
}

func (setup *FabricSetup) CloseSDK() {
	setup.sdk.Close()
}

The file is available here: blockchain/setup.go

At this stage, we only initialized a client that will communicate to a peer, a CA and an orderer. We also made a new channel and connected this peer to this channel. See the comments in the code for more information.

c. Test

To make sure that the client managed to initialize all his components, we will make a simple test with the network launched. In order to do this, we need to build the go code. Since we haven’t any main file we have to add one:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi main.go

package main

import (
	"fmt"
	"github.com/chainHero/heroes-service/blockchain"
	"os"
)

func main() {
	// Definition of the Fabric SDK properties
	fSetup := blockchain.FabricSetup{
		// Network parameters 
		OrdererID: "orderer.hf.chainhero.io",

		// Channel parameters
		ChannelID:     "chainhero",
		ChannelConfig: os.Getenv("GOPATH") + "/src/github.com/chainHero/heroes-service/fixtures/artifacts/chainhero.channel.tx",

		// Chaincode parameters
		ChainCodeID:     "heroes-service",
		ChaincodeGoPath: os.Getenv("GOPATH"),
		ChaincodePath:   "github.com/chainHero/heroes-service/chaincode/",
		OrgAdmin:        "Admin",
		OrgName:         "org1",
		ConfigFile:      "config.yaml",

		// User parameters
		UserName: "User1",
	}

	// Initialization of the Fabric SDK from the previously set properties
	err := fSetup.Initialize()
	if err != nil {
		fmt.Printf("Unable to initialize the Fabric SDK: %v\n", err)
		return
	}
	// Close SDK
	defer fSetup.CloseSDK()	
}

The file is available here: main.go

As you can see, we fixed the GOPATH of the environment if it’s not set. We will need this feature in order to compile the chaincode (we will see this in the next step).

The last thing to do, before starting the compilation, is to use a vendor directory that will contain all our dependencies. In our GOPATH we have Fabric SDK Go and maybe other projects. When we will try to compile our app, Golang search dependencies in our GOPATH, but first it checks if there is a vendor folder in the project. If the dependency is satisfied, then Golang doesn’t go looking in GOPATH or GOROOT. This is very useful when using several different versions of a dependency (some conflicts can happen, like multiple definitions of BCCSP in our case. We will handle this by using a tool like dep to flatten these dependencies in the vendor directory.

When you installed the SDK dependencies, DEP was automatically installed. If this is not the case, you can install it by reading the instructions available here: dep installation

Create a file called Gopkg.toml and copy this inside:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi Gopkg.toml
ignored = ["github.com/chainHero/heroes-service/chaincode"]

[[constraint]]
  # Release v1.0.0-alpha4
  name = "github.com/hyperledger/fabric-sdk-go"
  revision = "a906355f73d060d7bf95874a9e90dc17589edbb3"

This is a constraint for dep in order to specify that in our vendor we want the SDK Go to a specific version.

Save the file and then execute this command to synchronize the vendor directory with our project’s dependencies (this may take a while to proceed):

cd $GOPATH/src/github.com/chainHero/heroes-service && \
dep ensure

Now we can compile our application:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
go build

After some time, a new binary named main will appear at the root of the project. Try to start the binary like this:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
./heroes-service

Screenshot app started but no network

At this point, it won’t work because there is no network deployed that the SDK can talk with. We will first start the network and then launch the app again:

cd $GOPATH/src/github.com/chainHero/heroes-service/fixtures && \
docker-compose up -d && \
cd .. && \
./heroes-service

Screenshot app started with network

Note: you need to see “Initialization Successful”. If it’s not the case then something went wrong.

Alright! So we just initialised the SDK with our local network. In the next step, we will interact with a chaincode.

d. Clean up and Makefile

The Fabric SDK generates some files, like certificates, binaries and temporally files. Shutting down the network won’t fully clean up your environment and when you will need to start it again, these files will be reused to avoid building process. For development you can keep them to test quickly but for a real test, you need to clean up all and start from the beginning.

How clean up my environment ?

  • Shut down your network: cd $GOPATH/src/github.com/chainHero/heroes-service/fixtures && docker-compose down
  • Remove credential stores (defined in the config file, in the client.credentialStore section): rm -rf /tmp/heroes-service-*
  • Remove some docker containers and docker images not generated by the docker-compose command: docker rm -f -v `docker ps -a --no-trunc | grep "heroes-service" | cut -d ' ' -f 1` 2>/dev/null and docker rmi `docker images --no-trunc | grep "heroes-service" | cut -d ' ' -f 1` 2>/dev/null

How to be more efficient ?

We can automatize all these tasks in one single step. Also the build and start process can be automated. To do so, we will create a Makefile. First, ensure that you have the tool:

make --version

If make is not installed do (Ubuntu):

sudo apt install make

Then create a file named Makefile at the root of the project with this content:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi Makefile

.PHONY: all dev clean build env-up env-down run

all: clean build env-up run

dev: build run

##### BUILD
build:
	@echo "Build ..."
	@dep ensure
	@go build
	@echo "Build done"

##### ENV
env-up:
	@echo "Start environment ..."
	@cd fixtures && docker-compose up --force-recreate -d
	@echo "Environment up"

env-down:
	@echo "Stop environment ..."
	@cd fixtures && docker-compose down
	@echo "Environment down"

##### RUN
run:
	@echo "Start app ..."
	@./heroes-service

##### CLEAN
clean: env-down
	@echo "Clean up ..."
	@rm -rf /tmp/heroes-service-* heroes-service
	@docker rm -f -v `docker ps -a --no-trunc | grep "heroes-service" | cut -d ' ' -f 1` 2>/dev/null || true
	@docker rmi `docker images --no-trunc | grep "heroes-service" | cut -d ' ' -f 1` 2>/dev/null || true
	@echo "Clean up done"

The file is available here: Makefile

Now with the task all:

  1. the whole environment will be cleaned up,
  2. then our go program will be compiled,
  3. after which the network will be deployed and
  4. finally the app will be up and running.

To use it, go in the root of the project and use the make command:

  • Task all: make or make all
  • Task clean: clean up everything and put down the network (make clean)
  • Task build: just build the application (make build)
  • Task env-up: just make the network up (make env-up)

e. Install & instantiate the chaincode

We are almost there to use the blockchain system. But for now we haven’t set up any chaincode (smart contract) yet that will handle queries from our application. First, let’s create a new directory named chaincode and add a new file named main.go (this is the main entry point of our smart-contract)::

cd $GOPATH/src/github.com/chainHero/heroes-service && \
mkdir chaincode && \
vi chaincode/main.go

package main

import (
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// HeroesServiceChaincode implementation of Chaincode
type HeroesServiceChaincode struct {
}

// Init of the chaincode
// This function is called only one when the chaincode is instantiated.
// So the goal is to prepare the ledger to handle future requests.
func (t *HeroesServiceChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("########### HeroesServiceChaincode Init ###########")

	// Get the function and arguments from the request
	function, _ := stub.GetFunctionAndParameters()

	// Check if the request is the init function
	if function != "init" {
		return shim.Error("Unknown function call")
	}

	// Put in the ledger the key/value hello/world
	err := stub.PutState("hello", []byte("world"))
	if err != nil {
		return shim.Error(err.Error())
	}

	// Return a successful message
	return shim.Success(nil)
}

// Invoke
// All future requests named invoke will arrive here.
func (t *HeroesServiceChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("########### HeroesServiceChaincode Invoke ###########")

	// Get the function and arguments from the request
	function, args := stub.GetFunctionAndParameters()

	// Check whether it is an invoke request
	if function != "invoke" {
		return shim.Error("Unknown function call")
	}

	// Check whether the number of arguments is sufficient
	if len(args) < 1 {
		return shim.Error("The number of arguments is insufficient.")
	}
	
	// In order to manage multiple type of request, we will check the first argument.
	// Here we have one possible argument: query (every query request will read in the ledger without modification)
	if args[0] == "query" {
		return t.query(stub, args)
	}

	return shim.Error("Unknown action, check the first argument")
}

// query
// Every readonly functions in the ledger will be here
func (t *HeroesServiceChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	fmt.Println("########### HeroesServiceChaincode query ###########")

	// Check whether the number of arguments is sufficient
	if len(args) < 2 {
		return shim.Error("The number of arguments is insufficient.")
	}

	// Like the Invoke function, we manage multiple type of query requests with the second argument.
	// We also have only one possible argument: hello
	if args[1] == "hello" {

		// Get the state of the value matching the key hello in the ledger
		state, err := stub.GetState("hello")
		if err != nil {
			return shim.Error("Failed to get state of hello")
		}

		// Return this value in response
		return shim.Success(state)
	}

	// If the arguments given don’t match any function, we return an error
	return shim.Error("Unknown query action, check the second argument.")
}

func main() {
	// Start the chaincode and make it ready for futures requests
	err := shim.Start(new(HeroesServiceChaincode))
	if err != nil {
		fmt.Printf("Error starting Heroes Service chaincode: %s", err)
	}
}

The file is available here: chaincode/main.go

Note: the chaincode isn’t really related to the application, we can have one repository for the app and another for the chaincode. For your information, today the chaincode can also be written in other languages like Java.

For now, the chaincode does nothing extraordinary, just put the key/value hello/world in the ledger at initialisation. In addition, there is one function that we can call by an invoke: query hello. This function gets the state of the ledger, i.e. hello and give it in response. We will test this in the next step, after successfully install and instantiate the chaincode.

In order to install and instantiate the chaincode, we need to add some code in the application. Edit the blockchain/setup.go with those following lines:


package blockchain

import (
	"fmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
	mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	packager "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/gopackager"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/cauthdsl"
	"github.com/pkg/errors"
)

// FabricSetup implementation
type FabricSetup struct {
	ConfigFile      string
	OrgID           string
	OrdererID	string
	ChannelID       string
	ChainCodeID     string
	initialized     bool
	ChannelConfig   string
	ChaincodeGoPath string
	ChaincodePath   string
	OrgAdmin        string
	OrgName         string
	UserName        string
	client          *channel.Client
	admin           *resmgmt.Client
	sdk             *fabsdk.FabricSDK
	event           *event.Client
}
[...]

func (setup *FabricSetup) InstallAndInstantiateCC() error {

	// Create the chaincode package that will be sent to the peers
	ccPkg, err := packager.NewCCPackage(setup.ChaincodePath, setup.ChaincodeGoPath)
	if err != nil {
		return errors.WithMessage(err, "failed to create chaincode package")
	}
	fmt.Println("ccPkg created")

	// Install example cc to org peers
	installCCReq := resmgmt.InstallCCRequest{Name: setup.ChainCodeID, Path: setup.ChaincodePath, Version: "0", Package: ccPkg}
	_, err = setup.admin.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
	if err != nil {
		return errors.WithMessage(err, "failed to install chaincode")
	}
	fmt.Println("Chaincode installed")

	// Set up chaincode policy
	ccPolicy := cauthdsl.SignedByAnyMember([]string{"org1.hf.chainhero.io"})

	resp, err := setup.admin.InstantiateCC(setup.ChannelID, resmgmt.InstantiateCCRequest{Name: setup.ChainCodeID, Path: setup.ChaincodeGoPath, Version: "0", Args: [][]byte{[]byte("init")}, Policy: ccPolicy})
	if err != nil || resp.TransactionID == "" {
		return errors.WithMessage(err, "failed to instantiate the chaincode")
	}
	fmt.Println("Chaincode instantiated")

	// Channel client is used to query and execute transactions
	clientContext := setup.sdk.ChannelContext(setup.ChannelID, fabsdk.WithUser(setup.UserName))
	setup.client, err = channel.New(clientContext)
	if err != nil {
		return errors.WithMessage(err, "failed to create new channel client")
	}
	fmt.Println("Channel client created")

	// Creation of the client which will enables access to our channel events
	setup.event, err = event.New(clientContext)
	if err != nil {
		return errors.WithMessage(err, "failed to create new event client")
	}
	fmt.Println("Event client created")

	fmt.Println("Chaincode Installation & Instantiation Successful")
	return nil
}
[...]

The file is available here: blockchain/setup.go

Tips: take care of the chaincode version, if you want to update your chaincode, increment the version number set at the lines 103 and 113 of this setup.go file. Otherwise the network will keep the same chaincode.

We need now to modify our main.go file in order to call our new function

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi main.go

package main

import (
	"fmt"
	"github.com/chainHero/heroes-service/blockchain"
	"os"
)

func main() {
	// Definition of the Fabric SDK properties
	fSetup := blockchain.FabricSetup{
		// Network parameters
      	OrdererID: "orderer.hf.chainhero.io",
      	
		// Channel parameters
		ChannelID:     "chainhero",
		ChannelConfig: os.Getenv("GOPATH") + "/src/github.com/chainHero/heroes-service/fixtures/artifacts/chainhero.channel.tx",

		// Chaincode parameters
		ChainCodeID:     "heroes-service",
		ChaincodeGoPath: os.Getenv("GOPATH"),
		ChaincodePath:   "github.com/chainHero/heroes-service/chaincode/",
		OrgAdmin:        "Admin",
		OrgName:         "org1",
		ConfigFile:      "config.yaml",

		// User parameters
		UserName: "User1",
	}
[...]
	// Install and instantiate the chaincode
	err = fSetup.InstallAndInstantiateCC()
	if err != nil {
		fmt.Printf("Unable to install and instantiate the chaincode: %v\n", err)
		return
	}
}

The file is available here: main.go

We can test this, just with the make command setup in the previous step:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
make

Screenshot Chaincode installed and instantiated

Tips: the installation and the instantiation don’t need to be run at every start of the application. Only when we update the chaincode (and the chaincode version). A solution is to provide an argument when we run the application to tell to do this additional procedure before move on. Since in this tutorial we will clean up the environment every time we don’t really care about that.

f. Query the chaincode

Like a database, the chaincode is plugged and ready to answer. Let’s try the hello query.

We will put all query functions in a new file named query.go in the blockchain folder:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi blockchain/query.go

package blockchain

import (
	"fmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)

// QueryHello query the chaincode to get the state of hello
func (setup *FabricSetup) QueryHello() (string, error) {

	// Prepare arguments
	var args []string
	args = append(args, "invoke")
	args = append(args, "query")
	args = append(args, "hello")

	response, err := setup.client.Query(channel.Request{ChaincodeID: setup.ChainCodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1]), []byte(args[2])}})
	if err != nil {
		return "", fmt.Errorf("failed to query: %v", err)
	}

	return string(response.Payload), nil
}

The file is available here: blockchain/query.go

You can add the call to this new function in the main.go:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi main.go
func main() {

[...]

	// Query the chaincode
	response, err := fSetup.QueryHello()
	if err != nil {
		fmt.Printf("Unable to query hello on the chaincode: %v\n", err)
	} else {
		fmt.Printf("Response from the query hello: %s\n", response)
	}
}

The file is available here: main.go

Let’s try:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
make

Screenshot Query Hello

g. Change the ledger state

The next thing to do in order to make a basic tour of the Fabric SDK Go, is to make a request to the chaincode in order to change the ledger state.

First, we will add this ability in the chaincode. Edit the chaincode/main.go file:


[...]

// Invoke
// All future requests named invoke will arrive here.
func (t *HeroesServiceChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("########### HeroesServiceChaincode Invoke ###########")

	// Get the function and arguments from the request
	function, args := stub.GetFunctionAndParameters()

	// Check whether it is an invoke request
	if function != "invoke" {
		return shim.Error("Unknown function call")
	}

	// Check whether the number of arguments is sufficient
	if len(args) < 1 {
		return shim.Error("The number of arguments is insufficient.")
	}

	// In order to manage multiple type of request, we will check the first argument.
	// Here we have one possible argument: query (every query request will read in the ledger without modification)
	if args[0] == "query" {
		return t.query(stub, args)
	}

	// The update argument will manage all update in the ledger
	if args[0] == "invoke" {
		return t.invoke(stub, args)
	}

	// If the arguments given don’t match any function, we return an error
	return shim.Error("Unknown action, check the first argument")
}

[...]

// invoke
// Every functions that read and write in the ledger will be here
func (t *HeroesServiceChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	fmt.Println("########### HeroesServiceChaincode invoke ###########")

	if len(args) < 2 {
		return shim.Error("The number of arguments is insufficient.")
	}

	// Check if the ledger key is "hello" and process if it is the case. Otherwise it returns an error.
	if args[1] == "hello" && len(args) == 3 {

		// Write the new value in the ledger
		err := stub.PutState("hello", []byte(args[2]))
		if err != nil {
			return shim.Error("Failed to update state of hello")
		}

		// Notify listeners that an event "eventInvoke" have been executed (check line 19 in the file invoke.go)
		err = stub.SetEvent("eventInvoke", []byte{})
		if err != nil {
			return shim.Error(err.Error())
		}

		// Return this value in response
		return shim.Success(nil)
	}

	// If the arguments given don’t match any function, we return an error
	return shim.Error("Unknown invoke action, check the second argument.")
}

[...]

The file is available here: chaincode/main.go

From the application side, we add a new function to make the invocation of the chaincode. Add a file named invoke.go in the blockchain folder:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi blockchain/invoke.go
package blockchain

import (
	"fmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	"time"
)

// InvokeHello
func (setup *FabricSetup) InvokeHello(value string) (string, error) {

	// Prepare arguments
	var args []string
	args = append(args, "invoke")
	args = append(args, "invoke")
	args = append(args, "hello")
	args = append(args, value)

	eventID := "eventInvoke"

	// Add data that will be visible in the proposal, like a description of the invoke request
	transientDataMap := make(map[string][]byte)
	transientDataMap["result"] = []byte("Transient data in hello invoke")

	reg, notifier, err := setup.event.RegisterChaincodeEvent(setup.ChainCodeID, eventID)
	if err != nil {
		return "", err
	}
	defer setup.event.Unregister(reg)

	// Create a request (proposal) and send it
	response, err := setup.client.Execute(channel.Request{ChaincodeID: setup.ChainCodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1]), []byte(args[2]), []byte(args[3])}, TransientMap: transientDataMap})
	if err != nil {
		return "", fmt.Errorf("failed to move funds: %v", err)
	}

	// Wait for the result of the submission
	select {
	case ccEvent := <-notifier:
		fmt.Printf("Received CC event: %s\n", ccEvent)
	case <-time.After(time.Second * 20):
		return "", fmt.Errorf("did NOT receive CC event for eventId(%s)", eventID)
	}

	return string(response.TransactionID), nil
}

The file is available here: blockchain/invoke.go

You can then add the call to this function in the main.go:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
vi main.go
func main() {

[...]

	// Query the chaincode
	response, err := fSetup.QueryHello()
	if err != nil {
		fmt.Printf("Unable to query hello on the chaincode: %v\n", err)
	} else {
		fmt.Printf("Response from the query hello: %s\n", response)
	}

	// Invoke the chaincode
	txId, err := fSetup.InvokeHello("chainHero")
	if err != nil {
		fmt.Printf("Unable to invoke hello on the chaincode: %v\n", err)
	} else {
		fmt.Printf("Successfully invoke hello, transaction ID: %s\n", txId)
	}

	// Query again the chaincode
	response, err = fSetup.QueryHello()
	if err != nil {
		fmt.Printf("Unable to query hello on the chaincode: %v\n", err)
	} else {
		fmt.Printf("Response from the query hello: %s\n", response)
	}
}

The file is available here: main.go

Let’s try:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
make

Screenshot Invoke Hello

Note: this error message may appear: endorsement validation failed: Endorser Client Status Code: (3) ENDORSEMENT_MISMATCH. Description: ProposalResponsePayloads do not match. Since we have two peers, the SDK sends its request to the two directly and if it receives a different answer (which is quite possible because it’s an asynchronous system) then it returns this error. The best thing to do in these cases and start over again.

6. Make this in a web application

We also can make this usable for any user. The best choice is a web application and we are lucky because the Go language natively provides a web server handling HTTP requests and also templating for HTML.

For now, we have only two different actions: the query and the invocation of the hello value. Let’s make two HTML pages for each action. We add a web directory with three other directories:

We use the MVC (Model-View-Controller) to make it more readable. The Model will be the blockchain part, the View are templates and Controller are provided by functions in the controllers directory.

Populate each with the appropriate code (we also added Bootstrap to make the result a little prettier:

And finally, we change the main.go, in order to use the web interface instead of directly query the blockchain.

Run the app and go to localhost:3000/home.html:

cd $GOPATH/src/github.com/chainHero/heroes-service ; \
make

The home page make a query in the blockchain to get the value of the hello key and display it.

Screenshot Web Home Hello World

The request page has a form to change the hello value.

Screenshot Web Request Page

After a successful submission the transaction ID is given.

Screenshot Web Request Success

We can see the change by going back to the home page.

Screenshot Web Home Hello Superman

It’s the end for the first part. A more complex application is coming.

7. References

Posted by Antoine Chabert

49 comments

It would be helpful if you went over how to install the sdk and other libraries- `go get -u github.com/hyperledger/fabric-sdk-go` however this package does not include the shim pkg that is required. How do I install the shim pkg? thanks

Hi, you need the fabric repository for the chaincode part (in order to get the shim package.) : `go get -u github.com/hyperledger/fabric`
But, the `dep ensure` in this tutorial “ensure” that this packages are presents with the right version, so this steps are not required.

Hi, I happen to reach the site and get a lot of impression from what you implemented. I just got a little problem when I am trying to follow the steps.

When I typed “go build”, you said,

“After some time, a new binary named heroes-service will appear at the root of the project. Try to start the binary like this:

cd $GOPATH/src/github.com/chainHero/heroes-service && \
./heroes-service”

However, the new binary named “heroes-service” doesn’t appear.

So, I couldn’t proceed next steps.

Could you help me solve this problem?

Hi,
Indeed, the binary should be called “main” and not “heroes-service”. We will correct the explanations, thank you.

Hi,

Thanks for the great write up! However, I am having some problems while starting the binary. I have made some small changes and my binary is named xxnetwork.exe (I am working in Windows). When I run xxnetwork.exe, I am getting the following error. Can you please help me in fixing this?
SDK created
Unable to initialize the Fabric SDK: failed to create channel management client from Admin identity: failed to create resmgmt client due to context error: user not found

My setup.go file has the same content as mentioned in this page.
I have made some changes to my main.go file. The content are pasted below:

package main

import (
"fmt"
"xxdomain/xxnetwork/blockchain"
"os"
)

func main() {
// Definition of the Fabric SDK properties
fSetup := blockchain.FabricSetup{
// Network parameters
OrdererID: "orderer.hf.xxdomain.io",

// Channel parameters
ChannelID: "xxchannel",
ChannelConfig: os.Getenv("GOPATH") + "/src/xxdomain/xxnetwork/artifacts/xx.channel.tx",

// Chaincode parameters
ChainCodeID: "xx-service",
ChaincodeGoPath: os.Getenv("GOPATH"),
ChaincodePath: "xxdomain/xxnetwork/chaincode/",
OrgAdmin: "Admin",
OrgName: "xxorg1",
ConfigFile: "config.yaml",

// User parameters
UserName: "User1",
}

// Initialization of the Fabric SDK from the previously set properties
err := fSetup.Initialize()
if err != nil {
fmt.Printf("Unable to initialize the Fabric SDK: %v\n", err)
return
}

// Close SDK
defer fSetup.CloseSDK()
}

Hi,
Have you generated users in your network? Basically, does “User1” exist?

Hi,

Thanks for the response.
Which step in the tutorial creates the users. I followed the steps exactly as mentioned so I am sure the users were generated.
Could it be because I am using Windows instead of Ubuntu?

Thanks

Hi,
The user is provided by the network, so check your configuration “config.yaml”.
You need to check the crypto-config root path (“client.cryptoconfig.path”) and crypto path in every organisation (“organizations.YourOrganizartion.cryptoPath”).
The path needs to look like this: “peerOrganizations/org1.hf.chainhero.io/users/{username}@org1.hf.chainhero.io/msp”
Check also that “User1” exist: “peerOrganizations/org1.hf.chainhero.io/users/User1@org1.hf.chainhero.io”

Hi,

Tried your suggestions. The User1 does exist but still I keep getting the same error. Will try to do the same thing on a Ubuntu machine and see if it works.
Thanks for the help!

chainHero

So, if User1 doesn’t exist, the error come from your custom generated network. Have you follow this tutorial to do so ? https://chainhero.io/2018/04/tutorial-hyperledger-fabric-how-to-build-your-first-network/
Because there, we don’t generate users, all is given to make it easier.

Thanks for this excellent tutorial.
You may perhaps help me. I am trying to port your tutorial to my current prototype that works correctly. The network is an extension of the fabcar example (with mainly more peers). Thus, I changed the config.yaml to fit my fabric network and my credentials. Nevertheless, it seems that I made a mistake with the orderer configuration. An error message indicates that there is a time out error with the orderer.

Unable to initialize the Fabric SDK: failed to save channel: create channel failed: SendEnvelope failed: calling orderer '0.0.0.0:7050' failed: Orderer Client Status Code: (2) CONNECTION_FAILED. Description: dialing connection timed out [0.0.0.0:7050]

I tried different variants in the config.yaml

orderers:
orderer.example.com:
url: 0.0.0.0:7050 (or localhost:7050 or 127.0.0.1:7050)

In my docker-composer.yml, the orderer is

orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
command: orderer
ports:
- 7050:7050

Merci d’avance.

Hi,
This type of error can be caused by three factors: (from @troyronda)
– The orderer is actually down;
– Network issues or incorrect docker port mappings (it seems that this is not the case);
– TLS problems;
With TLS, you need to make sure the name being used by the client (to dial) matches the name in the certificate.
Note: the entity matchers section of the configuration allows you to override the names and mappings.

Thanks.
It was a wrong redirection in the docker-compose.yml at the level of the “volumes”. It did not find the ca certificates

Kushan Edirisooriya

how did you config couchdb database?

Hi,

Great tutorial.
One small mistake. In the first instance of main.go, the line
- OrgAdmin: setup.OrgAdmin
should be
- OrgAdmin: "Admin"
The code on github is OK whereas the code in the post is wrong.

Thanks

Hi,
Thanks a lot!

Hi,

I decide to give go-sdk a new chance and I have to say that you greatly improve your tutorial since v1.0.5 and it’s easy to follow. However, now that I want to plug your app on another fabric (with channel created and chaincode instantiated) that I created with a script, I encounter a lot a problems. I removed unnecessary function calls that creates channel and instantiates chaincode. I know I have to create a clientContext but I get the following error : Unable to initialize the Fabric SDK: failed to create new channel client: failed to create channel context: failed to get client context to create channel client: getting private key from cert failed: Failed getting key for SKI [[166 152 157 13 62 145 91 124 204 13 72 120 133 187 138 144 92 155 43 88 234 65 139 207 15 186 98 84 2 157 252 218]]: Key with SKI a6989d0d3e915b7ccc0d487885bb8a905c9b2b58ea418bcf0fba6254029dfcda not found in /tmp/blk-feed-service-store/keystore
I tried to dig into the sdk to find “admin” user is not found (that makes sense since none enroll function is called in your app). I also tried to find some official documentation because the sdk is a real maze. So, I would like to know how do you manage the identity of the users with the go SDK (It seems easier with nodeJs SDK) ?
Also it is not very clear if the TLS is activated in your app since everything seem good but the CA endpoint is start with ‘http’ instead ‘https’.

Thanks

Hi and thank you for your feedback.
Indeed, we do not use the CA in this application, only users pre-registered in the network (notably Admin and User1).
You can easily manage identities with the CA and SDK by following the examples available here: https://github.com/hyperledger/fabric-sdk-go/tree/master/test/integration/pkg/client/msp
For enrolment, there is this test: https://github.com/hyperledger/fabric-sdk-go/blob/master/test/integration/pkg/client/msp/enrollment_test.go
I hope this will help you.

Thank you for your quick answer. It doesn’t help me directly but it helps me find that usernames are sometimes converted to lower case (msp.GetIdentity(“Admin”) will be converted to “admin” whereas msp.GetSigningIdentity(“Admin”) will remain “Admin”)

Hi,
thanks for the amazing tutorial.

I am getting the following error, and I assume the problem is with the invoke, because the two queries(one after and one before the invoke()) are ok:

Chaincode Installation & Instantiation Successful
Response from the query hello: world
[fabsdk/fab] 2018/07/20 16:12:23 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = Unknown desc = chaincode error (status: 500, message: Unknown action, check the first argument)]
Unable to invoke hello on the chaincode: failed to move funds: Transaction processing for endorser [localhost:7051]: Chaincode status Code: (500) UNKNOWN. Description: Unknown action, check the first argument
Response from the query hello: world

Can you help me?
Thank you

As already told, the tutorial is extremely useful. It is the only resource that allows to jump in the use of fabric-sdk-go.
I have succeeded to migrate from your configuration to mine (4 peers, 4 couchdb, 1 org, 1 ca, 1 orderer).
I have succeeded to install and instantiate my chaincode (that works perfectly using invoke.js)
When requesting an Invoke through the SDK, I get the following error:
pgresolver.(*randomLBP).Choose -> WARN No available peer groups

ANy idea where I should dive? Thanks in advance

Kushan Edirisooriya

hi can you explain that how did you migrate from inmemory database

Sorry. I did find my answer. The MSP ID of Org1 has to be put in line 111 of blockchain/setupgo
ccPolicy := cauthdsl.SignedByAnyMember([]string{“Org1MSP”})
As a suggestion, could that not be pushed in the config.yaml rather than hardwiring it in the code?
In any case, many thanks for this hard work

Hi, I tried everything step-by-step but I think the fabric-sdk-go now needs go version 1.10? Also, I think this command was messing up: sudo tar -C /usr/local -xzf go1.10.linux-amd64.tar.gz because I was able to get it working by using: sudo tar -xzf go1.10.linux-amd64.tar.gz -C /usr/local

Hi, Thanks for this excellent tutorial, it’s really helpful.
Still I got this problem while using my own fabric network.

sdk client logs:

Unable to initialize the Fabric SDK: failed to save channel: create channel failed: SendEnvelope failed: calling orderer 'localhost:7050' failed: Orderer Server Status Code: (400) BAD_REQUEST. Description: error authorizing update: error validating DeltaSet: policy for [Group] /Channel/Application not satisfied: Failed to reach implicit threshold of 1 sub-policies, required 1 remaining

orderer container logs:

[cauthdsl] deduplicate -> ERRO 15a Principal deserialization failure (MSP dev1.isu.com is unknown) for identity 0a0c64......
and:
[orderer/common/broadcast] Handle -> WARN 16c [channel: guoxing] Rejecting broadcast of config message from 192.168.80.1:51292 because of error: error authorizing update: error validating DeltaSet: policy for [Group] /Channel/Application not satisfied: Failed to reach implicit threshold of 1 sub-policies, required 1 remaining

I am using “cryptogen” and “configtxgen” command to initialize my fabric network (following the fabric 1.2 doc ), I guess it’s some issues with the config files? Can you help me with this?
Thanks.

Hey,

Even I’m facing a similar isuue. I’ve narrowed it down to the OrgName supplied with the main.go file on root, but I think its something with how the configtx.yaml file was structured.

Let me know if you’ve figured it out please.

Jeppe Skajaa

Hey Guo and Nithin,

Did anyone of you figure out a solution to this problem? I facing the same problem.

Hi,
I want to setting on IBM Blockchain environment.
Is there a way?

Thanks for the tutorial. I am also getting this error when trying to generate artifacts myself:
“Failed to reach implicit threshold of 1 sub-policies, required 1 remaining”
It has something to do with the configtx.yaml file used to generate the artifacts. Can you please share the configtx.yaml file you are using.

Is it possible with hyperledger fabric to make a blockchain where everybody can see all the transactions but only a few can write? Is hyperledger a good option in this use case?

Rishi Broto Chakraborty

I modified the docker-compose to add 2 organizations with 5 peers in each organization. Each organization has its own channel. How do I modify the channel parameters in the main.go file to support this? Please help. I’m just a beginner with Hyperledger fabric and have just started experimenting with it. Thanks in advance.

Rishi Broto Chakraborty

I extended the organisation to have 5 peers. Until the blockchain setup it works fine. As soon as I get to install and instantiate the chaincode, I get the following error:

Environment up
Start app ...
SDK created
Ressource management client created
Channel created
Channel joined
Initialization Successful
ccPkg created
Chaincode installed
[fabsdk/fab] 2019/02/19 20:44:39 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
[fabsdk/fab] 2019/02/19 20:44:39 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
[fabsdk/fab] 2019/02/19 20:44:39 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
[fabsdk/fab] 2019/02/19 20:44:39 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
[fabsdk/fab] 2019/02/19 20:44:39 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
Unable to install and instantiate the chaincode: failed to instantiate the chaincode: sending deploy transaction proposal failed: Multiple errors occurred:
Transaction processing for endorser [localhost:8021]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded
Transaction processing for endorser [localhost:8001]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded
Transaction processing for endorser [localhost:8041]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded
Transaction processing for endorser [localhost:8031]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded
Transaction processing for endorser [localhost:8011]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded

Please help.

i have the same problem as yours ,how do you solve

Rishi Broto Chakraborty

Haven’t solved it yet. Tried updating GoLang too. But no solution yet.

Hassan Tarek

i got this error when code try to query Hello
Chaincode Installation & Instantiation Successful
[fabsdk/client] 2019/04/25 15:20:38 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
[fabsdk/client] 2019/04/25 15:20:39 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
[fabsdk/client] 2019/04/25 15:20:41 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
[fabsdk/client] 2019/04/25 15:20:47 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
[fabsdk/client] 2019/04/25 15:21:03 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
[fabsdk/client] 2019/04/25 15:21:42 UTC - fabricselection.(*Service).query -> WARN error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:
Unable to query hello on the chaincode: failed to query: Failed to get endorsing peers: error getting channel response for channel [mychannel]: Discovery status Code: (11) UNKNOWN. Description: error received from Discovery Server: error getting endorsers from target [localhost:7051]: failed constructing descriptor for chaincodes:

Solving failure:
(1) failed to list versions for https://github.com/milagro-crypto/amcl: fatal: could not read Username for ‘https://github.com’: terminal prompts disabled
: exit status 128
(2) failed to list versions for ssh://git@github.com/milagro-crypto/amcl: Permission denied (publickey).
fatal: Could not read from remote repository.
This is an output of my dep ensure -v, any idea how I can solve it?
Please make sure you have the correct access rights
and the repository exists.
: exit status 128
(3) failed to list versions for git://github.com/milagro-crypto/amcl: fatal: remote error:
Repository not found.
: exit status 128
(4) failed to list versions for http://github.com/milagro-crypto/amcl: fatal: could not read Username for ‘https://github.com’: terminal prompts disabled
: exit status 128

Hi, the code works fine except when i add these lines in:

// Invoke the chaincode
txId, err := fSetup.InvokeHello("chainHero")
if err != nil {
fmt.Printf("Unable to invoke hello on the chaincode: %v\n", err)
} else {
fmt.Printf("Successfully invoke hello, transaction ID: %s\n", txId)
}

OR

// Query again the chaincode
response, err = fSetup.QueryHello()
if err != nil {
fmt.Printf("Unable to query hello on the chaincode: %v\n", err)
} else {
fmt.Printf("Response from the query hello: %s\n", response)
}

which results in:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x88eb66]

goroutine 1 [running]:
github.com/chainHero/heroes-service/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/event.(*Client).RegisterChaincodeEvent(0x0, 0xba31cc, 0xe, 0xba133c, 0xb, 0x20, 0x2, 0x4, 0xc4204a3b20, 0x4c0ace)
/home/benjamin/go/src/github.com/chainHero/heroes-service/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/event/event.go:98 +0x26
github.com/chainHero/heroes-service/blockchain.(*FabricSetup).InvokeHello(0xc4204a3ea0, 0xb9fe3c, 0x9, 0x0, 0x0, 0x0, 0x0)
/home/benjamin/go/src/github.com/chainHero/heroes-service/blockchain/invoke.go:25 +0x257
main.main()
/home/benjamin/go/src/github.com/chainHero/heroes-service/main.go:53 +0x274
Makefile:27: recipe for target 'run' failed
make: *** [run] Error 2

holdonbush

I got this error when I run dep ensure:
ensure Solve(): unable to deduce repository and source type for "golang.org/x/crypto": unable to read metadata: unable to fetch raw metadata: failed HTTP request to URL "http://golang.org/x/crypto?go-get=1": Get http://golang.org/x/crypto?go-get=1: dial tcp 216.239.37.1:80: getsockopt: connection refused
can anyone help? thanks

This problem can be caused by proxy. You can run following code before dep ensure:
export http_proxy=127.0.0.1:1080
export https_proxy=127.0.0.1:1080

It works for me.

After that, I encounter another probem when I run go build:
# github.com/kongyixueyuan.com/kongyixueyuan/vendor/github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util
vendor/github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util/csp.go:47:8: cannot convert nil to type csr.KeyRequest
vendor/github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util/csp.go:132:37: cannot use req.KeyRequest (type *csr.KeyRequest) as type csr.KeyRequest in argument to getBCCSPKeyOpts
# github.com/kongyixueyuan.com/kongyixueyuan/vendor/github.com/zmap/zlint/lints
vendor/github.com/zmap/zlint/lints/result.go:75:9: undefined: strings.ReplaceAll
Makefile:17: recipe for target 'build' failed
make: *** [build] Error 2

I had tried diffferent verions of fabric-sdk-go. But, it’s useless.

can anyone help? thanks

Giacomo Tavola

Followed procedure since the very beginning and updated all requirement packages.

While launching build, I’ve got this message:
# github.com/chainHero/heroes-service/vendor/github.com/cloudflare/cfssl/csr
vendor/github.com/cloudflare/cfssl/csr/csr.go:272:26: cert.URIs undefined (type *x509.Certificate has no field or method URIs)
vendor/github.com/cloudflare/cfssl/csr/csr.go:387:7: tpl.URIs undefined (type x509.CertificateRequest has no field or method URIs)

And no main binary is generated, of course ./heroes-service command fails.

Could you advice ?
Thanks a lot

I cannot go for next step after “go build” because I’m getting error message below. Please help. Thanks.

vendor/github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab/channel.go:10:2: cannot find package "context" in any of:
/home/jinuk/go/src/github.com/chainHero/heroes-service/vendor/context (vendor tree)
/usr/lib/go-1.6/src/context (from $GOROOT)
/home/jinuk/go/src/context (from $GOPATH)
vendor/golang.org/x/sys/unix/affinity_linux.go:10:2: cannot find package "math/bits" in any of:
/home/jinuk/go/src/github.com/chainHero/heroes-service/vendor/math/bits (vendor tree)
/usr/lib/go-1.6/src/math/bits (from $GOROOT)
/home/jinuk/go/src/math/bits (from $GOPATH)
vendor/golang.org/x/net/http2/not_go111.go:10:2: cannot find package "net/http/httptrace" in any of:
/home/jinuk/go/src/github.com/chainHero/heroes-service/vendor/net/http/httptrace (vendor tree)
/usr/lib/go-1.6/src/net/http/httptrace (from $GOROOT)
/home/jinuk/go/src/net/http/httptrace (from $GOPATH)

Chris Smolen

Thank you for this excellent example. I was following along nicely until I went to build the main.go
---Error when Building: Cannot convert nil to type csr.KeyRequest
vendor/github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util/csp.go:47:8
AND
vendor/github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util/csp.go:132:37

and I notice that csp.go was modified 18 days ago. Any ideas?

Kushan Edirisooriya

this is working fine .. it worked ….
in main.go you might use wrong values for following attributes

fSetup := blockchain.FabricSetup{
ChannelConfig:
ChaincodeGoPath:
ChaincodePath:
}

Random Guy

Hey Chris,

I ran into that error too and it looks like there’s a JIRA issue open for it – https://jira.hyperledger.org/browse/FABG-903

I ran into another error with the statsd package which was resolved by using a specific version of go-kit.

cd $GOPATH/src/github.com/go-kit/kit
git fetch --tags
git checkout v0.8.0

When i run the go file i met some problems when run the main.go
--Error : get the wrong serialization msp.Serialization , we want sdk.msp.Serialization .
It seems looks like the version of package .
I use dep ensure to install the dependencies(constraint the fabric sdk go version 1.0 alpha4) but i track the log ,it seems that the dep find the newest go package protobuf (install two versions, one from github/golang and the other from go organizaion).
I’m trying to degrade the version of protobuf but it doesn’t work.
Is there any one can help me?
Thanks a lot!

Hi when i’m trying to run the project ,i got below error
Error can't not get the right name , got msp.Serialization , want sdk.msp.Serialization

Is there any one can help me?

@Chris Smolen, I think dep is deprecated from sometime this year. Use Go modules instead and you wont have that problem

Hi,

I have used your tutorial to develop my app, but i’m facing a trouble. When i initialize my app (with go run main.go), the http server app is initialize, but after some seconds (let’s say 20 seconds), my app crashes, having the following errors:
On server side (docker logs):
peer0.org1.app.com | 2021-01-05 19:03:00.915 UTC [eventhub_producer] Chat -> ERRO 4ea error during Chat, stopping handler: rpc error: code = Canceled desc = context canceled
peer0.org1.app.com | 2021-01-05 19:03:00.915 UTC [eventhub_producer] deRegisterHandler -> DEBU 4eb deregistering event type: BLOCK
peer0.org1.app.com | 2021-01-05 19:03:00.923 UTC [eventhub_producer] validateEventMessage -> DEBU 4ec validating for signed event 0xc4226c7410
peer0.org1.app.com | 2021-01-05 19:03:00.927 UTC [eventhub_producer] registerHandler -> DEBU 4ed registering event type: BLOCK

On client side (go terminal):
[fabsdk/fab] 2021/01/05 19:03:00 UTC - connection.(*EventHubConnection).Receive -> ERRO Received error from stream: [rpc error: code = Unavailable desc = transport is closing]. Sending disconnected event.
[fabsdk/fab] 2021/01/05 19:03:00 UTC - client.(*Client).monitorConnection -> WARN Event client has disconnected. Details: rpc error: code = Unavailable desc = transport is closing
[fabsdk/fab] 2021/01/05 19:03:00 UTC - client.(*Client).monitorConnection -> WARN Attempting to reconnect...

Apparently, the client disconnects of server after this seconds and can’t reconnect. I haven’t found solutions and hope you can help me.
Thanks for your tutorial and waiting for your help!

if anyone is facing this problem, it was related to gRPC version. I resolve this utilizing the 1.13 version of gRPC

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.