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