github.com/kaituanwang/hyperledger@v2.0.1+incompatible/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>`_, `node.js <https://nodejs.org>`_,
     8  or `Java <https://java.com/en/>`_ that implements a prescribed interface.
     9  Chaincode runs in a secured Docker container isolated from the endorsing peer
    10  process. Chaincode initializes and manages the ledger state through transactions
    11  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". A chaincode can be invoked to update or query
    15  the ledger in a proposal transaction. Given the appropriate permission, a chaincode
    16  may invoke another chaincode, either in the same channel or in different channels, to access its state.
    17  Note that, if the called chaincode is on a different channel from the calling chaincode,
    18  only read query is allowed. That is, the called chaincode on a different channel is only a ``Query``,
    19  which does not participate in state validation checks in subsequent commit phase.
    20  
    21  In the following sections, we will explore chaincode through the eyes of an
    22  application developer. We'll present a simple chaincode sample application
    23  and walk through the purpose of each method in the Chaincode Shim API.
    24  
    25  Chaincode API
    26  -------------
    27  
    28  Every chaincode program must implement the ``Chaincode`` interface whose methods
    29  are called in response to received transactions. You can find the reference
    30  documentation of the Chaincode Shim API for different languages below:
    31  
    32    - `Go <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#Chaincode>`__
    33    - `node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/fabric-shim.ChaincodeInterface.html>`__
    34    - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/shim/Chaincode.html>`_
    35  
    36  In each language, the ``Invoke`` method is called by clients to submit transaction
    37  proposals. This method allows you to use the chaincode to read and write data on
    38  the channel ledger.
    39  
    40  You also need to include an ``Init`` method that will serve as the initialization
    41  function for your chaincode. This method will be called in order to initialize
    42  the chaincode when it is started or upgraded. By default, this function is never
    43  executed. However, you can use the chaincode definition to request that the ``Init``
    44  function be executed. If execution of ``Init`` is requested, fabric will ensure
    45  that ``Init`` is invoked before any other function and is only invoked once.
    46  This option provides you additional control over which users can initialize the
    47  chaincode and the ability to add initial data to the ledger. If you are using
    48  the peer CLI to approve the chaincode definition, use the ``--init-required``
    49  flag to request the execution of the ``Init`` function. Then call the ``Init``
    50  function by using the `peer chaincode invoke` command and passing the
    51  ``--isInit`` flag. If you are using the Fabric SDK for Node.js, visit
    52  `How to install and start your chaincode <https://hyperledger.github.io/fabric-sdk-node/master/tutorial-chaincode-lifecycle.html>`__. For more information, see :doc:`chaincode4noah`.
    53  
    54  The other interface in the chaincode "shim" APIs is the ``ChaincodeStubInterface``:
    55  
    56    - `Go <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStubInterface>`__
    57    - `node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/fabric-shim.ChaincodeStub.html>`__
    58    - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/shim/ChaincodeStub.html>`_
    59  
    60  which is used to access and modify the ledger, and to make invocations between
    61  chaincodes.
    62  
    63  In this tutorial using Go chaincode, we will demonstrate the use of these APIs
    64  by implementing a simple chaincode application that manages simple "assets".
    65  
    66  .. _Simple Asset Chaincode:
    67  
    68  Simple Asset Chaincode
    69  ----------------------
    70  Our application is a basic sample chaincode to create assets
    71  (key-value pairs) on the ledger.
    72  
    73  Choosing a Location for the Code
    74  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    75  
    76  If you haven't been doing programming in Go, you may want to make sure that
    77  you have :ref:`Golang` installed and your system properly configured.
    78  
    79  Now, you will want to create a directory for your chaincode application as a
    80  child directory of ``$GOPATH/src/``.
    81  
    82  To keep things simple, let's use the following command:
    83  
    84  .. code:: bash
    85  
    86    mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc
    87  
    88  Now, let's create the source file that we'll fill in with code:
    89  
    90  .. code:: bash
    91  
    92    touch sacc.go
    93  
    94  Housekeeping
    95  ^^^^^^^^^^^^
    96  
    97  First, let's start with some housekeeping. As with every chaincode, it implements the
    98  `Chaincode interface <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#Chaincode>`_
    99  in particular, ``Init`` and ``Invoke`` functions. So, let's add the Go import
   100  statements for the necessary dependencies for our chaincode. We'll import the
   101  chaincode shim package and the
   102  `peer protobuf package <https://godoc.org/github.com/hyperledger/fabric-protos-go/peer>`_.
   103  Next, let's add a struct ``SimpleAsset`` as a receiver for Chaincode shim functions.
   104  
   105  .. code:: go
   106  
   107      package main
   108  
   109      import (
   110      	"fmt"
   111  
   112      	"github.com/hyperledger/fabric-chaincode-go/shim"
   113      	"github.com/hyperledger/fabric-protos-go/peer"
   114      )
   115  
   116      // SimpleAsset implements a simple chaincode to manage an asset
   117      type SimpleAsset struct {
   118      }
   119  
   120  Initializing the Chaincode
   121  ^^^^^^^^^^^^^^^^^^^^^^^^^^
   122  
   123  Next, we'll implement the ``Init`` function.
   124  
   125  .. code:: go
   126  
   127    // Init is called during chaincode instantiation to initialize any data.
   128    func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   129  
   130    }
   131  
   132  .. note:: Note that chaincode upgrade also calls this function. When writing a
   133            chaincode that will upgrade an existing one, make sure to modify the ``Init``
   134            function appropriately. In particular, provide an empty "Init" method if there's
   135            no "migration" or nothing to be initialized as part of the upgrade.
   136  
   137  Next, we'll retrieve the arguments to the ``Init`` call using the
   138  `ChaincodeStubInterface.GetStringArgs <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub.GetStringArgs>`_
   139  function and check for validity. In our case, we are expecting a key-value pair.
   140  
   141    .. code:: go
   142  
   143      // Init is called during chaincode instantiation to initialize any
   144      // data. Note that chaincode upgrade also calls this function to reset
   145      // or to migrate data, so be careful to avoid a scenario where you
   146      // inadvertently clobber your ledger's data!
   147      func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   148        // Get the args from the transaction proposal
   149        args := stub.GetStringArgs()
   150        if len(args) != 2 {
   151          return shim.Error("Incorrect arguments. Expecting a key and a value")
   152        }
   153      }
   154  
   155  Next, now that we have established that the call is valid, we'll store the
   156  initial state in the ledger. To do this, we will call
   157  `ChaincodeStubInterface.PutState <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub.PutState>`_
   158  with the key and value passed in as the arguments. Assuming all went well,
   159  return a peer.Response object that indicates the initialization was a success.
   160  
   161  .. code:: go
   162  
   163    // Init is called during chaincode instantiation to initialize any
   164    // data. Note that chaincode upgrade also calls this function to reset
   165    // or to migrate data, so be careful to avoid a scenario where you
   166    // inadvertently clobber your ledger's data!
   167    func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   168      // Get the args from the transaction proposal
   169      args := stub.GetStringArgs()
   170      if len(args) != 2 {
   171        return shim.Error("Incorrect arguments. Expecting a key and a value")
   172      }
   173  
   174      // Set up any variables or assets here by calling stub.PutState()
   175  
   176      // We store the key and the value on the ledger
   177      err := stub.PutState(args[0], []byte(args[1]))
   178      if err != nil {
   179        return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
   180      }
   181      return shim.Success(nil)
   182    }
   183  
   184  Invoking the Chaincode
   185  ^^^^^^^^^^^^^^^^^^^^^^
   186  
   187  First, let's add the ``Invoke`` function's signature.
   188  
   189  .. code:: go
   190  
   191      // Invoke is called per transaction on the chaincode. Each transaction is
   192      // either a 'get' or a 'set' on the asset created by Init function. The 'set'
   193      // method may create a new asset by specifying a new key-value pair.
   194      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   195  
   196      }
   197  
   198  As with the ``Init`` function above, we need to extract the arguments from the
   199  ``ChaincodeStubInterface``. The ``Invoke`` function's arguments will be the
   200  name of the chaincode application function to invoke. In our case, our application
   201  will simply have two functions: ``set`` and ``get``, that allow the value of an
   202  asset to be set or its current state to be retrieved. We first call
   203  `ChaincodeStubInterface.GetFunctionAndParameters <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub.GetFunctionAndParameters>`_
   204  to extract the function name and the parameters to that chaincode application
   205  function.
   206  
   207  .. code:: go
   208  
   209      // Invoke is called per transaction on the chaincode. Each transaction is
   210      // either a 'get' or a 'set' on the asset created by Init function. The Set
   211      // method may create a new asset by specifying a new key-value pair.
   212      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   213      	// Extract the function and args from the transaction proposal
   214      	fn, args := stub.GetFunctionAndParameters()
   215  
   216      }
   217  
   218  Next, we'll validate the function name as being either ``set`` or ``get``, and
   219  invoke those chaincode application functions, returning an appropriate
   220  response via the ``shim.Success`` or ``shim.Error`` functions that will
   221  serialize the response into a gRPC protobuf message.
   222  
   223  .. code:: go
   224  
   225      // Invoke is called per transaction on the chaincode. Each transaction is
   226      // either a 'get' or a 'set' on the asset created by Init function. The Set
   227      // method may create a new asset by specifying a new key-value pair.
   228      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   229      	// Extract the function and args from the transaction proposal
   230      	fn, args := stub.GetFunctionAndParameters()
   231  
   232      	var result string
   233      	var err error
   234      	if fn == "set" {
   235      		result, err = set(stub, args)
   236      	} else {
   237      		result, err = get(stub, args)
   238      	}
   239      	if err != nil {
   240      		return shim.Error(err.Error())
   241      	}
   242  
   243      	// Return the result as success payload
   244      	return shim.Success([]byte(result))
   245      }
   246  
   247  Implementing the Chaincode Application
   248  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   249  
   250  As noted, our chaincode application implements two functions that can be
   251  invoked via the ``Invoke`` function. Let's implement those functions now.
   252  Note that as we mentioned above, to access the ledger's state, we will leverage
   253  the `ChaincodeStubInterface.PutState <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub.PutState>`_
   254  and `ChaincodeStubInterface.GetState <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#ChaincodeStub.GetState>`_
   255  functions of the chaincode shim API.
   256  
   257  .. code:: go
   258  
   259      // Set stores the asset (both key and value) on the ledger. If the key exists,
   260      // it will override the value with the new one
   261      func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   262      	if len(args) != 2 {
   263      		return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
   264      	}
   265  
   266      	err := stub.PutState(args[0], []byte(args[1]))
   267      	if err != nil {
   268      		return "", fmt.Errorf("Failed to set asset: %s", args[0])
   269      	}
   270      	return args[1], nil
   271      }
   272  
   273      // Get returns the value of the specified asset key
   274      func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   275      	if len(args) != 1 {
   276      		return "", fmt.Errorf("Incorrect arguments. Expecting a key")
   277      	}
   278  
   279      	value, err := stub.GetState(args[0])
   280      	if err != nil {
   281      		return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
   282      	}
   283      	if value == nil {
   284      		return "", fmt.Errorf("Asset not found: %s", args[0])
   285      	}
   286      	return string(value), nil
   287      }
   288  
   289  .. _Chaincode Sample:
   290  
   291  Pulling it All Together
   292  ^^^^^^^^^^^^^^^^^^^^^^^
   293  
   294  Finally, we need to add the ``main`` function, which will call the
   295  `shim.Start <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#Start>`_
   296  function. Here's the whole chaincode program source.
   297  
   298  .. code:: go
   299  
   300      package main
   301  
   302      import (
   303      	"fmt"
   304  
   305      	"github.com/hyperledger/fabric-chaincode-go/shim"
   306      	"github.com/hyperledger/fabric-protos-go/peer"
   307      )
   308  
   309      // SimpleAsset implements a simple chaincode to manage an asset
   310      type SimpleAsset struct {
   311      }
   312  
   313      // Init is called during chaincode instantiation to initialize any
   314      // data. Note that chaincode upgrade also calls this function to reset
   315      // or to migrate data.
   316      func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
   317      	// Get the args from the transaction proposal
   318      	args := stub.GetStringArgs()
   319      	if len(args) != 2 {
   320      		return shim.Error("Incorrect arguments. Expecting a key and a value")
   321      	}
   322  
   323      	// Set up any variables or assets here by calling stub.PutState()
   324  
   325      	// We store the key and the value on the ledger
   326      	err := stub.PutState(args[0], []byte(args[1]))
   327      	if err != nil {
   328      		return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
   329      	}
   330      	return shim.Success(nil)
   331      }
   332  
   333      // Invoke is called per transaction on the chaincode. Each transaction is
   334      // either a 'get' or a 'set' on the asset created by Init function. The Set
   335      // method may create a new asset by specifying a new key-value pair.
   336      func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
   337      	// Extract the function and args from the transaction proposal
   338      	fn, args := stub.GetFunctionAndParameters()
   339  
   340      	var result string
   341      	var err error
   342      	if fn == "set" {
   343      		result, err = set(stub, args)
   344      	} else { // assume 'get' even if fn is nil
   345      		result, err = get(stub, args)
   346      	}
   347      	if err != nil {
   348      		return shim.Error(err.Error())
   349      	}
   350  
   351      	// Return the result as success payload
   352      	return shim.Success([]byte(result))
   353      }
   354  
   355      // Set stores the asset (both key and value) on the ledger. If the key exists,
   356      // it will override the value with the new one
   357      func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   358      	if len(args) != 2 {
   359      		return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
   360      	}
   361  
   362      	err := stub.PutState(args[0], []byte(args[1]))
   363      	if err != nil {
   364      		return "", fmt.Errorf("Failed to set asset: %s", args[0])
   365      	}
   366      	return args[1], nil
   367      }
   368  
   369      // Get returns the value of the specified asset key
   370      func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
   371      	if len(args) != 1 {
   372      		return "", fmt.Errorf("Incorrect arguments. Expecting a key")
   373      	}
   374  
   375      	value, err := stub.GetState(args[0])
   376      	if err != nil {
   377      		return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
   378      	}
   379      	if value == nil {
   380      		return "", fmt.Errorf("Asset not found: %s", args[0])
   381      	}
   382      	return string(value), nil
   383      }
   384  
   385      // main function starts up the chaincode in the container during instantiate
   386      func main() {
   387      	if err := shim.Start(new(SimpleAsset)); err != nil {
   388      		fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
   389      	}
   390      }
   391  
   392  Building Chaincode
   393  ^^^^^^^^^^^^^^^^^^
   394  
   395  Now let's compile your chaincode.
   396  
   397  .. code:: bash
   398  
   399    go get -u github.com/hyperledger/fabric-chaincode-go
   400    go build
   401  
   402  Assuming there are no errors, now we can proceed to the next step, testing
   403  your chaincode.
   404  
   405  Testing Using dev mode
   406  ^^^^^^^^^^^^^^^^^^^^^^
   407  
   408  Normally chaincodes are started and maintained by peer. However in “dev
   409  mode", chaincode is built and started by the user. This mode is useful
   410  during chaincode development phase for rapid code/build/run/debug cycle
   411  turnaround.
   412  
   413  We start "dev mode" by leveraging pre-generated orderer and channel artifacts for
   414  a sample dev network.  As such, the user can immediately jump into the process
   415  of compiling chaincode and driving calls.
   416  
   417  Install Hyperledger Fabric Samples
   418  ----------------------------------
   419  
   420  If you haven't already done so, please :doc:`install`.
   421  
   422  Navigate to the ``chaincode-docker-devmode`` directory of the ``fabric-samples``
   423  clone:
   424  
   425  .. code:: bash
   426  
   427    cd chaincode-docker-devmode
   428  
   429  Now open three terminals and navigate to your ``chaincode-docker-devmode``
   430  directory in each.
   431  
   432  Terminal 1 - Start the network
   433  ------------------------------
   434  
   435  .. code:: bash
   436  
   437      docker-compose -f docker-compose-simple.yaml up
   438  
   439  The above starts the network with the ``SingleSampleMSPSolo`` orderer profile and
   440  launches the peer in "dev mode".  It also launches two additional containers -
   441  one for the chaincode environment and a CLI to interact with the chaincode.  The
   442  commands for create and join channel are embedded in the CLI container, so we
   443  can jump immediately to the chaincode calls.
   444  
   445  - Note: the peer is not using TLS because the dev mode does not work with TLS.
   446  
   447  Terminal 2 - Build & start the chaincode
   448  ----------------------------------------
   449  
   450  .. code:: bash
   451  
   452    docker exec -it chaincode sh
   453  
   454  You should see the following:
   455  
   456  .. code:: sh
   457  
   458    /opt/gopath/src/chaincode $
   459  
   460  Now, compile your chaincode:
   461  
   462  .. code:: sh
   463  
   464    cd sacc
   465    go build
   466  
   467  Now run the chaincode:
   468  
   469  .. code:: sh
   470  
   471    CORE_CHAINCODE_ID_NAME=mycc:0 CORE_PEER_TLS_ENABLED=false ./sacc -peer.address peer:7052
   472  
   473  The chaincode is started with peer and chaincode logs indicating successful registration with the peer.
   474  Note that at this stage the chaincode is not associated with any channel. This is done in subsequent steps
   475  using the ``instantiate`` command.
   476  
   477  Terminal 3 - Use the chaincode
   478  ------------------------------
   479  
   480  Even though you are in ``--peer-chaincodedev`` mode, you still have to install the
   481  chaincode so the life-cycle system chaincode can go through its checks normally.
   482  This requirement may be removed in future when in ``--peer-chaincodedev`` mode.
   483  
   484  We'll leverage the CLI container to drive these calls.
   485  
   486  .. code:: bash
   487  
   488    docker exec -it cli bash
   489  
   490  .. code:: bash
   491  
   492    peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
   493    peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
   494  
   495  Now issue an invoke to change the value of "a" to "20".
   496  
   497  .. code:: bash
   498  
   499    peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
   500  
   501  Finally, query ``a``.  We should see a value of ``20``.
   502  
   503  .. code:: bash
   504  
   505    peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
   506  
   507  Testing new chaincode
   508  ---------------------
   509  
   510  By default, we mount only ``sacc``.  However, you can easily test different
   511  chaincodes by adding them to the ``chaincode`` subdirectory and relaunching
   512  your network.  At this point they will be accessible in your ``chaincode`` container.
   513  
   514  Chaincode access control
   515  ------------------------
   516  
   517  Chaincode can utilize the client (submitter) certificate for access
   518  control decisions by calling the GetCreator() function. Additionally
   519  the Go shim provides extension APIs that extract client identity
   520  from the submitter's certificate that can be used for access control decisions,
   521  whether that is based on client identity itself, or the org identity,
   522  or on a client identity attribute.
   523  
   524  For example an asset that is represented as a key/value may include the
   525  client's identity as part of the value (for example as a JSON attribute
   526  indicating that asset owner), and only this client may be authorized
   527  to make updates to the key/value in the future. The client identity
   528  library extension APIs can be used within chaincode to retrieve this
   529  submitter information to make such access control decisions.
   530  
   531  See the `client identity (CID) library documentation <https://github.com/hyperledger/fabric-chaincode-go/blob/master/pkg/cid/README.md>`_
   532  for more details.
   533  
   534  To add the client identity shim extension to your chaincode as a dependency, see :ref:`vendoring`.
   535  
   536  .. _vendoring:
   537  
   538  Managing external dependencies for chaincode written in Go
   539  ----------------------------------------------------------
   540  Your Go chaincode requires packages (like the chaincode shim) that are not part
   541  of the Go standard library. These packages must be included in your chaincode
   542  package.
   543  
   544  There are `many tools available <https://github.com/golang/go/wiki/PackageManagementTools>`__
   545  for managing (or "vendoring") these dependencies.  The following demonstrates how to use
   546  ``govendor``:
   547  
   548  .. code:: bash
   549  
   550    govendor init
   551    govendor add +external  // Add all external package, or
   552    govendor add github.com/external/pkg // Add specific external package
   553  
   554  This imports the external dependencies into a local ``vendor`` directory.
   555  If you are vendoring the Fabric shim or shim extensions, clone the
   556  Fabric repository to your $GOPATH/src/github.com/hyperledger directory,
   557  before executing the govendor commands.
   558  
   559  Once dependencies are vendored in your chaincode directory, ``peer chaincode package``
   560  and ``peer chaincode install`` operations will then include code associated with the
   561  dependencies into the chaincode package.
   562  
   563  .. Licensed under Creative Commons Attribution 4.0 International License
   564     https://creativecommons.org/licenses/by/4.0/