github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/docs/source/chaincode4ade.rst (about)

     1  Chaincode for Developers
     2  ========================
     3  
     4  What is Chaincode?
     5  ------------------
     6  
     7  Chaincode is a program, written in `Go <https://golang.org>`_ that implements a
     8  prescribed interface. Eventually, other programming languages such as Java,
     9  will be supported. Chaincode runs in a secured Docker container isolated from
    10  the endorsing peer process. Chaincode initializes and manages the ledger state
    11  through transactions submitted by applications.
    12  
    13  A chaincode typically handles business logic agreed to by members of the
    14  network, so it similar to a "smart contract". Ledger state created by
    15  a chaincode is scoped exclusively to that chaincode and can't be accessed
    16  directly by another chaincode. Given the appropriate permission, a chaincode may
    17  invoke another chaincode to access its state within the same network.
    18  
    19  In the following sections, we will explore chaincode through the eyes of an
    20  application developer. We'll present a simple chaincode sample application
    21  and walk through the purpose of each method in the Chaincode Shim API.
    22  
    23  Chaincode API
    24  -------------
    25  
    26  Every chaincode program must implement the
    27  `Chaincode interface <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#Chaincode>`_
    28  whose methods are called in response to received transactions.
    29  In particular the ``Init`` method is called when a
    30  chaincode receives an ``instantiate`` or ``upgrade`` transaction so that the
    31  chaincode may perform any necessary initialization, including initialization of
    32  application state. The ``Invoke`` method is called in response to receiving an
    33  ``invoke`` transaction to process transaction proposals.
    34  
    35  The other interface in the chaincode "shim" APIs is the
    36  `ChaincodeStubInterface <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub>`_
    37  which is used to access and modify the ledger, and to make invocations between
    38  chaincodes.
    39  
    40  In this tutorial, we will demonstrate the use of these APIs by implementing a
    41  simple chaincode application that manages simple "assets".
    42  
    43  .. _Simple Asset Chaincode:
    44  
    45  Simple Asset Chaincode
    46  ----------------------
    47  Our application is a basic sample chaincode to create assets
    48  (key-value pairs) on the ledger.
    49  
    50  Choosing a Location for the Code
    51  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    52  
    53  If you haven't been doing programming in Go, you may want to make sure that
    54  you have :ref:`Golang` installed and your system properly configured.
    55  
    56  Now, you will want to create a directory for your chaincode application as a
    57  child directory of ``$GOPATH/src/``.
    58  
    59  To keep things simple, let's use the following command:
    60  
    61  .. code:: bash
    62  
    63    mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc
    64  
    65  Now, let's create the source file that we'll fill in with code:
    66  
    67  .. code:: bash
    68  
    69    touch sacc.go
    70  
    71  Housekeeping
    72  ^^^^^^^^^^^^
    73  
    74  First, let's start with some housekeeping. As with every chaincode, it implements the
    75  `Chaincode interface <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#Chaincode>`_
    76  in particular, ``Init`` and ``Invoke`` functions. So, let's add the go import
    77  statements for the necessary dependencies for our chaincode. We'll import the
    78  chaincode shim package and the
    79  `peer protobuf package <http://godoc.org/github.com/hyperledger/fabric/protos/peer>`_.
    80  Next, let's add a struct ``SimpleAsset`` as a receiver for Chaincode shim functions.
    81  
    82  .. code:: go
    83  
    84      package main
    85  
    86      import (
    87      	"fmt"
    88  
    89      	"github.com/hyperledger/fabric/core/chaincode/shim"
    90      	"github.com/hyperledger/fabric/protos/peer"
    91      )
    92  
    93      // SimpleAsset implements a simple chaincode to manage an asset
    94      type SimpleAsset struct {
    95      }
    96  
    97  Initializing the Chaincode
    98  ^^^^^^^^^^^^^^^^^^^^^^^^^^
    99  
   100  Next, we'll implement the ``Init`` function.
   101  
   102  .. code:: go
   103  
   104    // Init is called during chaincode instantiation to initialize any data.
   105    func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   106  
   107    }
   108  
   109  .. note:: Note that chaincode upgrade also calls this function. When writing a
   110            chaincode that will upgrade an existing one, make sure to modify the ``Init``
   111            function appropriately. In particular, provide an empty "Init" method if there's
   112            no "migration" or nothing to be initialized as part of the upgrade.
   113  
   114  Next, we'll retrieve the arguments to the ``Init`` call using the
   115  `ChaincodeStubInterface.GetStringArgs <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.GetStringArgs>`_
   116  function and check for validity. In our case, we are expecting a key-value pair.
   117  
   118    .. code:: go
   119  
   120      // Init is called during chaincode instantiation to initialize any
   121      // data. Note that chaincode upgrade also calls this function to reset
   122      // or to migrate data, so be careful to avoid a scenario where you
   123      // inadvertently clobber your ledger's data!
   124      func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   125        // Get the args from the transaction proposal
   126        args := stub.GetStringArgs()
   127        if len(args) != 2 {
   128          return shim.Error("Incorrect arguments. Expecting a key and a value")
   129        }
   130      }
   131  
   132  Next, now that we have established that the call is valid, we'll store the
   133  initial state in the ledger. To do this, we will call
   134  `ChaincodeStubInterface.PutState <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.PutState>`_
   135  with the key and value passed in as the arguments. Assuming all went well,
   136  return a peer.Response object that indicates the initialization was a success.
   137  
   138  .. code:: go
   139  
   140    // Init is called during chaincode instantiation to initialize any
   141    // data. Note that chaincode upgrade also calls this function to reset
   142    // or to migrate data, so be careful to avoid a scenario where you
   143    // inadvertently clobber your ledger's data!
   144    func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   145      // Get the args from the transaction proposal
   146      args := stub.GetStringArgs()
   147      if len(args) != 2 {
   148        return shim.Error("Incorrect arguments. Expecting a key and a value")
   149      }
   150  
   151      // Set up any variables or assets here by calling stub.PutState()
   152  
   153      // We store the key and the value on the ledger
   154      err := stub.PutState(args[0], []byte(args[1]))
   155      if err != nil {
   156        return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
   157      }
   158      return shim.Success(nil)
   159    }
   160  
   161  Invoking the Chaincode
   162  ^^^^^^^^^^^^^^^^^^^^^^
   163  
   164  First, let's add the ``Invoke`` function's signature.
   165  
   166  .. code:: go
   167  
   168      // Invoke is called per transaction on the chaincode. Each transaction is
   169      // either a 'get' or a 'set' on the asset created by Init function. The 'set'
   170      // method may create a new asset by specifying a new key-value pair.
   171      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   172  
   173      }
   174  
   175  As with the ``Init`` function above, we need to extract the arguments from the
   176  ``ChaincodeStubInterface``. The ``Invoke`` function's arguments will be the
   177  name of the chaincode application function to invoke. In our case, our application
   178  will simply have two functions: ``set`` and ``get``, that allow the value of an
   179  asset to be set or its current state to be retrieved. We first call
   180  `ChaincodeStubInterface.GetFunctionAndParameters <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.GetFunctionAndParameters>`_
   181  to extract the function name and the parameters to that chaincode application
   182  function.
   183  
   184  .. code:: go
   185  
   186      // Invoke is called per transaction on the chaincode. Each transaction is
   187      // either a 'get' or a 'set' on the asset created by Init function. The Set
   188      // method may create a new asset by specifying a new key-value pair.
   189      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   190      	// Extract the function and args from the transaction proposal
   191      	fn, args := stub.GetFunctionAndParameters()
   192  
   193      }
   194  
   195  Next, we'll validate the function name as being either ``set`` or ``get``, and
   196  invoke those chaincode application functions, returning an appropriate
   197  response via the ``shim.Success`` or ``shim.Error`` functions that will
   198  serialize the response into a gRPC protobuf message.
   199  
   200  .. code:: go
   201  
   202      // Invoke is called per transaction on the chaincode. Each transaction is
   203      // either a 'get' or a 'set' on the asset created by Init function. The Set
   204      // method may create a new asset by specifying a new key-value pair.
   205      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   206      	// Extract the function and args from the transaction proposal
   207      	fn, args := stub.GetFunctionAndParameters()
   208  
   209      	var result string
   210      	var err error
   211      	if fn == "set" {
   212      		result, err = set(stub, args)
   213      	} else {
   214      		result, err = get(stub, args)
   215      	}
   216      	if err != nil {
   217      		return shim.Error(err.Error())
   218      	}
   219  
   220      	// Return the result as success payload
   221      	return shim.Success([]byte(result))
   222      }
   223  
   224  Implementing the Chaincode Application
   225  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   226  
   227  As noted, our chaincode application implements two functions that can be
   228  invoked via the ``Invoke`` function. Let's implement those functions now.
   229  Note that as we mentioned above, to access the ledger's state, we will leverage
   230  the `ChaincodeStubInterface.PutState <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.PutState>`_
   231  and `ChaincodeStubInterface.GetState <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub.GetState>`_
   232  functions of the chaincode shim API.
   233  
   234  .. code:: go
   235  
   236      // Set stores the asset (both key and value) on the ledger. If the key exists,
   237      // it will override the value with the new one
   238      func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   239      	if len(args) != 2 {
   240      		return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
   241      	}
   242  
   243      	err := stub.PutState(args[0], []byte(args[1]))
   244      	if err != nil {
   245      		return "", fmt.Errorf("Failed to set asset: %s", args[0])
   246      	}
   247      	return args[1], nil
   248      }
   249  
   250      // Get returns the value of the specified asset key
   251      func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   252      	if len(args) != 1 {
   253      		return "", fmt.Errorf("Incorrect arguments. Expecting a key")
   254      	}
   255  
   256      	value, err := stub.GetState(args[0])
   257      	if err != nil {
   258      		return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
   259      	}
   260      	if value == nil {
   261      		return "", fmt.Errorf("Asset not found: %s", args[0])
   262      	}
   263      	return string(value), nil
   264      }
   265  
   266  .. _Chaincode Sample:
   267  
   268  Pulling it All Together
   269  ^^^^^^^^^^^^^^^^^^^^^^^
   270  
   271  Finally, we need to add the ``main`` function, which will call the
   272  `shim.Start <http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#Start>`_
   273  function. Here's the whole chaincode program source.
   274  
   275  .. code:: go
   276  
   277      package main
   278  
   279      import (
   280      	"fmt"
   281  
   282      	"github.com/hyperledger/fabric/core/chaincode/shim"
   283      	"github.com/hyperledger/fabric/protos/peer"
   284      )
   285  
   286      // SimpleAsset implements a simple chaincode to manage an asset
   287      type SimpleAsset struct {
   288      }
   289  
   290      // Init is called during chaincode instantiation to initialize any
   291      // data. Note that chaincode upgrade also calls this function to reset
   292      // or to migrate data.
   293      func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   294      	// Get the args from the transaction proposal
   295      	args := stub.GetStringArgs()
   296      	if len(args) != 2 {
   297      		return shim.Error("Incorrect arguments. Expecting a key and a value")
   298      	}
   299  
   300      	// Set up any variables or assets here by calling stub.PutState()
   301  
   302      	// We store the key and the value on the ledger
   303      	err := stub.PutState(args[0], []byte(args[1]))
   304      	if err != nil {
   305      		return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
   306      	}
   307      	return shim.Success(nil)
   308      }
   309  
   310      // Invoke is called per transaction on the chaincode. Each transaction is
   311      // either a 'get' or a 'set' on the asset created by Init function. The Set
   312      // method may create a new asset by specifying a new key-value pair.
   313      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   314      	// Extract the function and args from the transaction proposal
   315      	fn, args := stub.GetFunctionAndParameters()
   316  
   317      	var result string
   318      	var err error
   319      	if fn == "set" {
   320      		result, err = set(stub, args)
   321      	} else { // assume 'get' even if fn is nil
   322      		result, err = get(stub, args)
   323      	}
   324      	if err != nil {
   325      		return shim.Error(err.Error())
   326      	}
   327  
   328      	// Return the result as success payload
   329      	return shim.Success([]byte(result))
   330      }
   331  
   332      // Set stores the asset (both key and value) on the ledger. If the key exists,
   333      // it will override the value with the new one
   334      func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   335      	if len(args) != 2 {
   336      		return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
   337      	}
   338  
   339      	err := stub.PutState(args[0], []byte(args[1]))
   340      	if err != nil {
   341      		return "", fmt.Errorf("Failed to set asset: %s", args[0])
   342      	}
   343      	return args[1], nil
   344      }
   345  
   346      // Get returns the value of the specified asset key
   347      func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   348      	if len(args) != 1 {
   349      		return "", fmt.Errorf("Incorrect arguments. Expecting a key")
   350      	}
   351  
   352      	value, err := stub.GetState(args[0])
   353      	if err != nil {
   354      		return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
   355      	}
   356      	if value == nil {
   357      		return "", fmt.Errorf("Asset not found: %s", args[0])
   358      	}
   359      	return string(value), nil
   360      }
   361  
   362      // main function starts up the chaincode in the container during instantiate
   363      func main() {
   364      	if err := shim.Start(new(SimpleAsset)); err != nil {
   365      		fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
   366      	}
   367      }
   368  
   369  Building Chaincode
   370  ^^^^^^^^^^^^^^^^^^
   371  
   372  Now let's compile your chaincode.
   373  
   374  .. code:: bash
   375  
   376    go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
   377    go build --tags nopkcs11
   378  
   379  Assuming there are no errors, now we can proceed to the next step, testing
   380  your chaincode.
   381  
   382  Testing Using dev mode
   383  ^^^^^^^^^^^^^^^^^^^^^^
   384  
   385  Normally chaincodes are started and maintained by peer. However in “dev
   386  mode", chaincode is built and started by the user. This mode is useful
   387  during chaincode development phase for rapid code/build/run/debug cycle
   388  turnaround.
   389  
   390  We start "dev mode" by leveraging pre-generated orderer and channel artifacts for
   391  a sample dev network.  As such, the user can immediately jump into the process
   392  of compiling chaincode and driving calls.
   393  
   394  Install Hyperledger Fabric Samples
   395  ----------------------
   396  
   397  If you haven't already done so, please install the :doc:`samples`.
   398  
   399  Navigate to the ``chaincode-docker-devmode`` directory of the ``fabric-samples``
   400  clone:
   401  
   402  .. code:: bash
   403  
   404    cd chaincode-docker-devmode
   405  
   406  Download Docker images
   407  ----------------------
   408  
   409  We need four Docker images in order for "dev mode" to run against the supplied
   410  docker compose script.  If you installed the ``fabric-samples`` repo clone and
   411  followed the instructions to :ref:`download-platform-specific-binaries`, then
   412  you should have the necessary Docker images installed locally.
   413  
   414  .. note:: If you choose to manually pull the images then you must retag them as
   415            ``latest``.
   416  
   417  Issue a ``docker images`` command to reveal your local Docker Registry.  You
   418  should see something similar to following:
   419  
   420  .. code:: bash
   421  
   422    docker images
   423    REPOSITORY                     TAG                                  IMAGE ID            CREATED             SIZE
   424    hyperledger/fabric-tools       latest                               e09f38f8928d        4 hours ago         1.32 GB
   425    hyperledger/fabric-tools       x86_64-1.0.0                         e09f38f8928d        4 hours ago         1.32 GB
   426    hyperledger/fabric-orderer     latest                               0df93ba35a25        4 hours ago         179 MB
   427    hyperledger/fabric-orderer     x86_64-1.0.0                         0df93ba35a25        4 hours ago         179 MB
   428    hyperledger/fabric-peer        latest                               533aec3f5a01        4 hours ago         182 MB
   429    hyperledger/fabric-peer        x86_64-1.0.0                         533aec3f5a01        4 hours ago         182 MB
   430    hyperledger/fabric-ccenv       latest                               4b70698a71d3        4 hours ago         1.29 GB
   431    hyperledger/fabric-ccenv       x86_64-1.0.0                         4b70698a71d3        4 hours ago         1.29 GB
   432  
   433  .. note:: If you retrieved the images through the :ref:`download-platform-specific-binaries`,
   434            then you will see additional images listed.  However, we are only concerned with
   435            these four.
   436  
   437  Now open three terminals and navigate to your ``chaincode-docker-devmode``
   438  directory in each.
   439  
   440  Terminal 1 - Start the network
   441  ------------------------------
   442  
   443  .. code:: bash
   444  
   445      docker-compose -f docker-compose-simple.yaml up
   446  
   447  The above starts the network with the ``SingleSampleMSPSolo`` orderer profile and
   448  launches the peer in "dev mode".  It also launches two additional containers -
   449  one for the chaincode environment and a CLI to interact with the chaincode.  The
   450  commands for create and join channel are embedded in the CLI container, so we
   451  can jump immediately to the chaincode calls.
   452  
   453  Terminal 2 - Build & start the chaincode
   454  ----------------------------------------
   455  
   456  .. code:: bash
   457  
   458    docker exec -it chaincode bash
   459  
   460  You should see the following:
   461  
   462  .. code:: bash
   463  
   464    root@d2629980e76b:/opt/gopath/src/chaincode#
   465  
   466  Now, compile your chaincode:
   467  
   468  .. code:: bash
   469  
   470    cd sacc
   471    go build
   472  
   473  Now run the chaincode:
   474  
   475  .. code:: bash
   476  
   477    CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
   478  
   479  The chaincode is started with peer and chaincode logs indicating successful registration with the peer.
   480  Note that at this stage the chaincode is not associated with any channel. This is done in subsequent steps
   481  using the ``instantiate`` command.
   482  
   483  Terminal 3 - Use the chaincode
   484  ------------------------------
   485  
   486  Even though you are in ``--peer-chaincodedev`` mode, you still have to install the
   487  chaincode so the life-cycle system chaincode can go through its checks normally.
   488  This requirement may be removed in future when in ``--peer-chaincodedev`` mode.
   489  
   490  We'll leverage the CLI container to drive these calls.
   491  
   492  .. code:: bash
   493  
   494    docker exec -it cli bash
   495  
   496  .. code:: bash
   497  
   498    peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
   499    peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
   500  
   501  Now issue an invoke to change the value of "a" to "20".
   502  
   503  .. code:: bash
   504  
   505    peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
   506  
   507  Finally, query ``a``.  We should see a value of ``20``.
   508  
   509  .. code:: bash
   510  
   511    peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
   512  
   513  Testing new chaincode
   514  ---------------------
   515  
   516  By default, we mount only ``sacc``.  However, you can easily test different
   517  chaincodes by adding them to the ``chaincode`` subdirectory and relaunching
   518  your network.  At this point they will be accessible in your ``chaincode`` container.
   519  
   520  .. Licensed under Creative Commons Attribution 4.0 International License
   521     https://creativecommons.org/licenses/by/4.0/