github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/docs/source/chaincode4ade.rst (about)

     1  Writing Your First Chaincode
     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 separate process from the peer and initializes and manages
    10  the ledger state through transactions submitted by applications.
    11  
    12  A chaincode typically handles business logic agreed to by members of the
    13  network, so it similar to a "smart contract". A chaincode can be invoked to update or query
    14  the ledger in a proposal transaction. Given the appropriate permission, a chaincode
    15  may invoke another chaincode, either in the same channel or in different channels, to access its state.
    16  Note that, if the called chaincode is on a different channel from the calling chaincode,
    17  only read query is allowed. That is, the called chaincode on a different channel is only a ``Query``,
    18  which does not participate in state validation checks in subsequent commit phase.
    19  
    20  In the following sections, we will explore chaincode through the eyes of an
    21  application developer. We'll present a asset-transfer chaincode sample walkthrough,
    22  and the purpose of each method in the Fabric Contract API. If you
    23  are a network operator who is deploying a chaincode to running network,
    24  visit the :doc:`deploy_chaincode` tutorial and the :doc:`chaincode_lifecycle`
    25  concept topic.
    26  
    27  This tutorial provides an overview of the high level APIs provided by the Fabric Contract API.
    28  To learn more about developing smart contracts using the Fabric contract API, visit the :doc:`developapps/smartcontract` topic.
    29  
    30  Fabric Contract API
    31  -------------------
    32  
    33  The ``fabric-contract-api`` provides the contract interface, a high level API for application developers to implement Smart Contracts.
    34  Within Hyperledger Fabric, Smart Contracts are also known as Chaincode. Working with this API provides a high level entry point to writing business logic.
    35  Documentation of the Fabric Contract API for different languages can be found at the links below:
    36  
    37    - `Go <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi>`__
    38    - `Node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/>`__
    39    - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/contract/package-summary.html>`__
    40  
    41  
    42  Note that when using the contract api, each chaincode function that is called is passed a transaction context "ctx", from which
    43  you can get the chaincode stub (GetStub() ), which has functions to access the ledger (e.g. GetState() ) and make requests
    44  to update the ledger (e.g. PutState() ). You can learn more at the language-specific links below.
    45  
    46    - `Go <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#Chaincode>`__
    47    - `Node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/fabric-shim.ChaincodeInterface.html>`__
    48    - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/shim/Chaincode.html>`__
    49  
    50  
    51  In this tutorial using Go chaincode, we will demonstrate the use of these APIs
    52  by implementing a asset-transfer chaincode application that manages simple "assets".
    53  
    54  .. _Asset Transfer Chaincode:
    55  
    56  Asset Transfer Chaincode
    57  ------------------------
    58  Our application is a basic sample chaincode to initialize a ledger with assets, create, read, update, and delete assets, check to see
    59  if an asset exists, and transfer assets from one owner to another.
    60  
    61  Choosing a Location for the Code
    62  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    63  
    64  If you haven't been doing programming in Go, you may want to make sure that
    65  you have `Go <https://golang.org>`_ installed and your system properly configured. We assume
    66  you are using a version that supports modules.
    67  
    68  Now, you will want to create a directory for your chaincode application.
    69  
    70  To keep things simple, let's use the following command:
    71  
    72  .. code:: bash
    73  
    74    // atcc is shorthand for asset transfer chaincode
    75       mkdir atcc && cd atcc
    76  
    77  Now, let's create the module and the source file that we'll fill in with code:
    78  
    79  .. code:: bash
    80  
    81    go mod init atcc
    82    touch atcc.go
    83  
    84  Housekeeping
    85  ^^^^^^^^^^^^
    86  
    87  First, let's start with some housekeeping. As with every chaincode, it implements the
    88  `fabric-contract-api interface <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi>`_,
    89  so let's add the Go import statements for the necessary dependencies for our chaincode. We'll import the
    90  fabric contract api package and define our SmartContract.
    91  
    92  .. code:: go
    93  
    94    package main
    95  
    96    import (
    97      "fmt"
    98      "log"
    99      "github.com/hyperledger/fabric-contract-api-go/contractapi"
   100    )
   101  
   102    // SmartContract provides functions for managing an Asset
   103       type SmartContract struct {
   104       contractapi.Contract
   105       }
   106  
   107  Next, let's add a struct ``Asset`` to represent simple assets on the ledger.
   108  Note the JSON annotations, which will be used to marshal the asset to JSON which is stored on the ledger.
   109  
   110  .. code:: go
   111  
   112    // Asset describes basic details of what makes up a simple asset
   113       type Asset struct {
   114        ID             string `json:"ID"`
   115        Color          string `json:"color"`
   116        Size           int    `json:"size"`
   117        Owner          string `json:"owner"`
   118        AppraisedValue int    `json:"appraisedValue"`
   119       }
   120  
   121  Initializing the Chaincode
   122  ^^^^^^^^^^^^^^^^^^^^^^^^^^
   123  
   124  Next, we'll implement the ``InitLedger`` function to populate the ledger with some initial data.
   125  
   126  .. code:: go
   127  
   128    // InitLedger adds a base set of assets to the ledger
   129       func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
   130          assets := []Asset{
   131            {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
   132            {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
   133            {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
   134            {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
   135            {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
   136            {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
   137          }
   138  
   139       for _, asset := range assets {
   140          assetJSON, err := json.Marshal(asset)
   141          if err != nil {
   142            return err
   143          }
   144  
   145          err = ctx.GetStub().PutState(asset.ID, assetJSON)
   146          if err != nil {
   147            return fmt.Errorf("failed to put to world state. %v", err)
   148          }
   149        }
   150  
   151        return nil
   152      }
   153  
   154  Next, we write a function to create an asset on the ledger that does not yet exist. When writing chaincode, it
   155  is a good idea to check for the existence of something on the ledger prior to taking an action on it, as is demonstrated
   156  in the ``CreateAsset`` function below.
   157  
   158  
   159  .. code:: go
   160  
   161      // CreateAsset issues a new asset to the world state with given details.
   162         func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
   163          exists, err := s.AssetExists(ctx, id)
   164          if err != nil {
   165            return err
   166          }
   167          if exists {
   168            return fmt.Errorf("the asset %s already exists", id)
   169          }
   170  
   171          asset := Asset{
   172            ID:             id,
   173            Color:          color,
   174            Size:           size,
   175            Owner:          owner,
   176            AppraisedValue: appraisedValue,
   177          }
   178          assetJSON, err := json.Marshal(asset)
   179          if err != nil {
   180            return err
   181          }
   182  
   183          return ctx.GetStub().PutState(id, assetJSON)
   184        }
   185  
   186  Now that we have populated the ledger with some initial assets and created an asset,
   187  let's write a function ``ReadAsset`` that allows us to read an asset from the ledger.
   188  
   189  .. code:: go
   190  
   191    // ReadAsset returns the asset stored in the world state with given id.
   192       func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
   193        assetJSON, err := ctx.GetStub().GetState(id)
   194        if err != nil {
   195          return nil, fmt.Errorf("failed to read from world state: %v", err)
   196        }
   197        if assetJSON == nil {
   198          return nil, fmt.Errorf("the asset %s does not exist", id)
   199        }
   200  
   201        var asset Asset
   202        err = json.Unmarshal(assetJSON, &asset)
   203        if err != nil {
   204          return nil, err
   205        }
   206  
   207        return &asset, nil
   208      }
   209  
   210  Now that we have assets on our ledger we can interact with, let's write a chaincode function
   211  ``UpdateAsset`` that allows us to update attributes of the asset that we are allowed to change.
   212  
   213  .. code:: go
   214  
   215    // UpdateAsset updates an existing asset in the world state with provided parameters.
   216       func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
   217          exists, err := s.AssetExists(ctx, id)
   218          if err != nil {
   219            return err
   220          }
   221          if !exists {
   222            return fmt.Errorf("the asset %s does not exist", id)
   223          }
   224  
   225          // overwriting original asset with new asset
   226          asset := Asset{
   227            ID:             id,
   228            Color:          color,
   229            Size:           size,
   230            Owner:          owner,
   231            AppraisedValue: appraisedValue,
   232          }
   233          assetJSON, err := json.Marshal(asset)
   234          if err != nil {
   235            return err
   236          }
   237  
   238          return ctx.GetStub().PutState(id, assetJSON)
   239      }
   240  
   241  There may be cases where we need the ability to delete an asset from the ledger,
   242  so let's write a ``DeleteAsset`` function to handle that requirement.
   243  
   244  .. code:: go
   245  
   246    // DeleteAsset deletes an given asset from the world state.
   247       func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
   248          exists, err := s.AssetExists(ctx, id)
   249          if err != nil {
   250            return err
   251          }
   252          if !exists {
   253            return fmt.Errorf("the asset %s does not exist", id)
   254          }
   255  
   256          return ctx.GetStub().DelState(id)
   257       }
   258  
   259  
   260  We said earlier that is was a good idea to check to see if an asset exists before
   261  taking an action on it, so let's write a function called ``AssetExists`` to implement that requirement.
   262  
   263  .. code:: go
   264  
   265    // AssetExists returns true when asset with given ID exists in world state
   266       func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
   267          assetJSON, err := ctx.GetStub().GetState(id)
   268          if err != nil {
   269            return false, fmt.Errorf("failed to read from world state: %v", err)
   270          }
   271  
   272          return assetJSON != nil, nil
   273        }
   274  
   275  Next, we'll write a function we'll call ``TransferAsset`` that enables the transfer of an asset from one owner to another.
   276  
   277  .. code:: go
   278  
   279    // TransferAsset updates the owner field of asset with given id in world state.
   280       func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
   281          asset, err := s.ReadAsset(ctx, id)
   282          if err != nil {
   283            return err
   284          }
   285  
   286          asset.Owner = newOwner
   287          assetJSON, err := json.Marshal(asset)
   288          if err != nil {
   289            return err
   290          }
   291  
   292          return ctx.GetStub().PutState(id, assetJSON)
   293        }
   294  
   295  Let's write a function we'll call ``GetAllAssets`` that enables the querying of the ledger to
   296  return all of the assets on the ledger.
   297  
   298  .. code:: go
   299  
   300    // GetAllAssets returns all assets found in world state
   301       func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
   302    // range query with empty string for startKey and endKey does an
   303    // open-ended query of all assets in the chaincode namespace.
   304        resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
   305        if err != nil {
   306          return nil, err
   307        }
   308        defer resultsIterator.Close()
   309  
   310        var assets []*Asset
   311        for resultsIterator.HasNext() {
   312          queryResponse, err := resultsIterator.Next()
   313          if err != nil {
   314            return nil, err
   315          }
   316  
   317          var asset Asset
   318          err = json.Unmarshal(queryResponse.Value, &asset)
   319          if err != nil {
   320            return nil, err
   321          }
   322          assets = append(assets, &asset)
   323        }
   324  
   325        return assets, nil
   326      }
   327  
   328  .. _Chaincode Sample:
   329  
   330  
   331  .. Note:: The full chaincode sample below is presented as a way to
   332            to keep this tutorial as clear and straightforward as possible. In a
   333            real-world implementation, it is likely that packages will be segmented
   334            where a ``main`` package imports the chaincode package to allow for easy unit testing.
   335            To see what this looks like, see the asset-transfer `Go chaincode <https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-basic/chaincode-go>`__
   336            in fabric-samples. If you look at ``assetTransfer.go``, you will see that
   337            it contains ``package main`` and imports ``package chaincode`` defined in ``smartcontract.go`` and
   338            located at ``fabric-samples/asset-transfer-basic/chaincode-go/chaincode/``.
   339  
   340  
   341  
   342  Pulling it All Together
   343  ^^^^^^^^^^^^^^^^^^^^^^^
   344  
   345  Finally, we need to add the ``main`` function, which will call the
   346  `ContractChaincode.Start <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi#ContractChaincode.Start>`_
   347  function. Here's the whole chaincode program source.
   348  
   349  .. code:: go
   350  
   351    package main
   352  
   353    import (
   354      "encoding/json"
   355      "fmt"
   356      "log"
   357  
   358      "github.com/hyperledger/fabric-contract-api-go/contractapi"
   359    )
   360  
   361    // SmartContract provides functions for managing an Asset
   362       type SmartContract struct {
   363          contractapi.Contract
   364        }
   365  
   366    // Asset describes basic details of what makes up a simple asset
   367       type Asset struct {
   368          ID             string `json:"ID"`
   369          Color          string `json:"color"`
   370          Size           int    `json:"size"`
   371          Owner          string `json:"owner"`
   372          AppraisedValue int    `json:"appraisedValue"`
   373        }
   374  
   375    // InitLedger adds a base set of assets to the ledger
   376       func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
   377        assets := []Asset{
   378          {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
   379          {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
   380          {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
   381          {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
   382          {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
   383          {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
   384        }
   385  
   386        for _, asset := range assets {
   387          assetJSON, err := json.Marshal(asset)
   388          if err != nil {
   389            return err
   390          }
   391  
   392          err = ctx.GetStub().PutState(asset.ID, assetJSON)
   393          if err != nil {
   394            return fmt.Errorf("failed to put to world state. %v", err)
   395          }
   396        }
   397  
   398        return nil
   399      }
   400  
   401    // CreateAsset issues a new asset to the world state with given details.
   402       func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
   403        exists, err := s.AssetExists(ctx, id)
   404        if err != nil {
   405          return err
   406        }
   407        if exists {
   408          return fmt.Errorf("the asset %s already exists", id)
   409        }
   410  
   411        asset := Asset{
   412          ID:             id,
   413          Color:          color,
   414          Size:           size,
   415          Owner:          owner,
   416          AppraisedValue: appraisedValue,
   417        }
   418        assetJSON, err := json.Marshal(asset)
   419        if err != nil {
   420          return err
   421        }
   422  
   423        return ctx.GetStub().PutState(id, assetJSON)
   424      }
   425  
   426    // ReadAsset returns the asset stored in the world state with given id.
   427       func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
   428        assetJSON, err := ctx.GetStub().GetState(id)
   429        if err != nil {
   430          return nil, fmt.Errorf("failed to read from world state: %v", err)
   431        }
   432        if assetJSON == nil {
   433          return nil, fmt.Errorf("the asset %s does not exist", id)
   434        }
   435  
   436        var asset Asset
   437        err = json.Unmarshal(assetJSON, &asset)
   438        if err != nil {
   439          return nil, err
   440        }
   441  
   442        return &asset, nil
   443      }
   444  
   445    // UpdateAsset updates an existing asset in the world state with provided parameters.
   446       func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
   447        exists, err := s.AssetExists(ctx, id)
   448        if err != nil {
   449          return err
   450        }
   451        if !exists {
   452          return fmt.Errorf("the asset %s does not exist", id)
   453        }
   454  
   455        // overwriting original asset with new asset
   456        asset := Asset{
   457          ID:             id,
   458          Color:          color,
   459          Size:           size,
   460          Owner:          owner,
   461          AppraisedValue: appraisedValue,
   462        }
   463        assetJSON, err := json.Marshal(asset)
   464        if err != nil {
   465          return err
   466        }
   467  
   468        return ctx.GetStub().PutState(id, assetJSON)
   469      }
   470  
   471      // DeleteAsset deletes an given asset from the world state.
   472      func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
   473        exists, err := s.AssetExists(ctx, id)
   474        if err != nil {
   475          return err
   476        }
   477        if !exists {
   478          return fmt.Errorf("the asset %s does not exist", id)
   479        }
   480  
   481        return ctx.GetStub().DelState(id)
   482      }
   483  
   484    // AssetExists returns true when asset with given ID exists in world state
   485       func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
   486        assetJSON, err := ctx.GetStub().GetState(id)
   487        if err != nil {
   488          return false, fmt.Errorf("failed to read from world state: %v", err)
   489        }
   490  
   491        return assetJSON != nil, nil
   492      }
   493  
   494    // TransferAsset updates the owner field of asset with given id in world state.
   495       func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
   496        asset, err := s.ReadAsset(ctx, id)
   497        if err != nil {
   498          return err
   499        }
   500  
   501        asset.Owner = newOwner
   502        assetJSON, err := json.Marshal(asset)
   503        if err != nil {
   504          return err
   505        }
   506  
   507        return ctx.GetStub().PutState(id, assetJSON)
   508      }
   509  
   510    // GetAllAssets returns all assets found in world state
   511       func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
   512    // range query with empty string for startKey and endKey does an
   513    // open-ended query of all assets in the chaincode namespace.
   514        resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
   515        if err != nil {
   516          return nil, err
   517        }
   518        defer resultsIterator.Close()
   519  
   520        var assets []*Asset
   521        for resultsIterator.HasNext() {
   522          queryResponse, err := resultsIterator.Next()
   523          if err != nil {
   524            return nil, err
   525          }
   526  
   527          var asset Asset
   528          err = json.Unmarshal(queryResponse.Value, &asset)
   529          if err != nil {
   530            return nil, err
   531          }
   532          assets = append(assets, &asset)
   533        }
   534  
   535        return assets, nil
   536      }
   537  
   538      func main() {
   539        assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
   540        if err != nil {
   541          log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
   542        }
   543  
   544        if err := assetChaincode.Start(); err != nil {
   545          log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
   546        }
   547      }
   548  
   549  Chaincode access control
   550  ------------------------
   551  
   552  Chaincode can utilize the client (submitter) certificate for access
   553  control decisions with ``ctx.GetStub().GetCreator()``. Additionally
   554  the Fabric Contract API provides extension APIs that extract client identity
   555  from the submitter's certificate that can be used for access control decisions,
   556  whether that is based on client identity itself, or the org identity,
   557  or on a client identity attribute.
   558  
   559  For example an asset that is represented as a key/value may include the
   560  client's identity as part of the value (for example as a JSON attribute
   561  indicating that asset owner), and only this client may be authorized
   562  to make updates to the key/value in the future. The client identity
   563  library extension APIs can be used within chaincode to retrieve this
   564  submitter information to make such access control decisions.
   565  
   566  
   567  .. _vendoring:
   568  
   569  Managing external dependencies for chaincode written in Go
   570  ----------------------------------------------------------
   571  Your Go chaincode depends on Go packages (like the chaincode shim) that are not
   572  part of the standard library. The source to these packages must be included in
   573  your chaincode package when it is installed to a peer. If you have structured
   574  your chaincode as a module, the easiest way to do this is to "vendor" the
   575  dependencies with ``go mod vendor`` before packaging your chaincode.
   576  
   577  .. code:: bash
   578  
   579    go mod tidy
   580    go mod vendor
   581  
   582  This places the external dependencies for your chaincode into a local ``vendor``
   583  directory.
   584  
   585  Once dependencies are vendored in your chaincode directory, ``peer chaincode package``
   586  and ``peer chaincode install`` operations will then include code associated with the
   587  dependencies into the chaincode package.
   588  
   589  .. Licensed under Creative Commons Attribution 4.0 International License
   590     https://creativecommons.org/licenses/by/4.0/