fabric-docs-i18n icon indicating copy to clipboard operation
fabric-docs-i18n copied to clipboard

[ja_JP] Tutorials / Writing Your First Chaincode

Open shimos opened this issue 2 years ago • 0 comments

Original HTML: https://hyperledger-fabric.readthedocs.io/en/release-2.5/chaincode4ade.html Original Source: https://github.com/hyperledger/fabric/blob/e1e8e2e52aa4fc543360d245fe6554a0eaf81183/docs/source/chaincode4ade.rst

diff --git a/docs/source/chaincode4ade.rst b/docs/source/chaincode4ade.rst
index 44253bdc4..28b4ba480 100644
--- a/docs/source/chaincode4ade.rst
+++ b/docs/source/chaincode4ade.rst
@@ -72,7 +72,7 @@ To keep things simple, let's use the following command:
 .. code:: bash
 
   // atcc is shorthand for asset transfer chaincode
-     mkdir atcc && cd atcc
+  mkdir atcc && cd atcc
 
 Now, let's create the module and the source file that we'll fill in with code:
 
@@ -95,28 +95,35 @@ fabric contract api package and define our SmartContract.
 
   import (
     "fmt"
+    "encoding/json"
     "log"
     "github.com/hyperledger/fabric-contract-api-go/contractapi"
   )
 
   // SmartContract provides functions for managing an Asset
-     type SmartContract struct {
-     contractapi.Contract
-     }
+  type SmartContract struct {
+    contractapi.Contract
+  }
 
 Next, let's add a struct ``Asset`` to represent simple assets on the ledger.
 Note the JSON annotations, which will be used to marshal the asset to JSON which is stored on the ledger.
+JSON though is not a deterministic data format - the order of elements can change, whilst still representing the same data semantically.
+The challenge, therefore, is to be able to generate a consistent set of JSON.
+Below is also shown a good approach to achieve consistency which consists of creating an asset object struct following alphabetic order.
 
 .. code:: go
 
   // Asset describes basic details of what makes up a simple asset
-     type Asset struct {
-      ID             string `json:"ID"`
-      Color          string `json:"color"`
-      Size           int    `json:"size"`
-      Owner          string `json:"owner"`
-      AppraisedValue int    `json:"appraisedValue"`
-     }
+  // Insert struct field in alphabetic order => to achieve determinism accross languages
+  // golang keeps the order when marshal to json but doesn't order automatically
+
+  type Asset struct {
+    AppraisedValue int    `json:"AppraisedValue"`
+    Color          string `json:"Color"`
+    ID             string `json:"ID"`
+    Owner          string `json:"Owner"`
+    Size           int    `json:"Size"`
+  }
 
 Initializing the Chaincode
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -126,31 +133,31 @@ Next, we'll implement the ``InitLedger`` function to populate the ledger with so
 .. code:: go
 
   // InitLedger adds a base set of assets to the ledger
-     func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
-        assets := []Asset{
-          {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
-          {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
-          {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
-          {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
-          {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
-          {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
-        }
-
-     for _, asset := range assets {
-        assetJSON, err := json.Marshal(asset)
-        if err != nil {
+  func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+    assets := []Asset{
+      {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
+      {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
+      {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
+      {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
+      {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
+      {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
+    }
+
+    for _, asset := range assets {
+      assetJSON, err := json.Marshal(asset)
+      if err != nil {
           return err
-        }
+      }
 
-        err = ctx.GetStub().PutState(asset.ID, assetJSON)
-        if err != nil {
+      err = ctx.GetStub().PutState(asset.ID, assetJSON)
+      if err != nil {
           return fmt.Errorf("failed to put to world state. %v", err)
-        }
       }
-
-      return nil
     }
 
+    return nil
+  }
+
 Next, we write a function to create an asset on the ledger that does not yet exist. When writing chaincode, it
 is a good idea to check for the existence of something on the ledger prior to taking an action on it, as is demonstrated
 in the ``CreateAsset`` function below.
@@ -159,29 +166,29 @@ in the ``CreateAsset`` function below.
 .. code:: go
 
     // CreateAsset issues a new asset to the world state with given details.
-       func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
-        exists, err := s.AssetExists(ctx, id)
-        if err != nil {
-          return err
-        }
-        if exists {
-          return fmt.Errorf("the asset %s already exists", id)
-        }
-
-        asset := Asset{
-          ID:             id,
-          Color:          color,
-          Size:           size,
-          Owner:          owner,
-          AppraisedValue: appraisedValue,
-        }
-        assetJSON, err := json.Marshal(asset)
-        if err != nil {
-          return err
-        }
+    func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+      exists, err := s.AssetExists(ctx, id)
+      if err != nil {
+        return err
+      }
+      if exists {
+        return fmt.Errorf("the asset %s already exists", id)
+      }
 
-        return ctx.GetStub().PutState(id, assetJSON)
+      asset := Asset{
+        ID:             id,
+        Color:          color,
+        Size:           size,
+        Owner:          owner,
+        AppraisedValue: appraisedValue,
       }
+      assetJSON, err := json.Marshal(asset)
+      if err != nil {
+        return err
+      }
+
+      return ctx.GetStub().PutState(id, assetJSON)
+    }
 
 Now that we have populated the ledger with some initial assets and created an asset,
 let's write a function ``ReadAsset`` that allows us to read an asset from the ledger.
@@ -189,72 +196,72 @@ let's write a function ``ReadAsset`` that allows us to read an asset from the le
 .. code:: go
 
   // ReadAsset returns the asset stored in the world state with given id.
-     func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
-      assetJSON, err := ctx.GetStub().GetState(id)
-      if err != nil {
-        return nil, fmt.Errorf("failed to read from world state: %v", err)
-      }
-      if assetJSON == nil {
-        return nil, fmt.Errorf("the asset %s does not exist", id)
-      }
-
-      var asset Asset
-      err = json.Unmarshal(assetJSON, &asset)
-      if err != nil {
-        return nil, err
-      }
+  func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
+    assetJSON, err := ctx.GetStub().GetState(id)
+    if err != nil {
+      return nil, fmt.Errorf("failed to read from world state: %v", err)
+    }
+    if assetJSON == nil {
+      return nil, fmt.Errorf("the asset %s does not exist", id)
+    }
 
-      return &asset, nil
+    var asset Asset
+    err = json.Unmarshal(assetJSON, &asset)
+    if err != nil {
+      return nil, err
     }
 
+    return &asset, nil
+  }
+
 Now that we have assets on our ledger we can interact with, let's write a chaincode function
 ``UpdateAsset`` that allows us to update attributes of the asset that we are allowed to change.
 
 .. code:: go
 
   // UpdateAsset updates an existing asset in the world state with provided parameters.
-     func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
-        exists, err := s.AssetExists(ctx, id)
-        if err != nil {
-          return err
-        }
-        if !exists {
-          return fmt.Errorf("the asset %s does not exist", id)
-        }
-
-        // overwriting original asset with new asset
-        asset := Asset{
-          ID:             id,
-          Color:          color,
-          Size:           size,
-          Owner:          owner,
-          AppraisedValue: appraisedValue,
-        }
-        assetJSON, err := json.Marshal(asset)
-        if err != nil {
-          return err
-        }
+  func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+    exists, err := s.AssetExists(ctx, id)
+    if err != nil {
+      return err
+    }
+    if !exists {
+      return fmt.Errorf("the asset %s does not exist", id)
+    }
 
-        return ctx.GetStub().PutState(id, assetJSON)
+    // overwriting original asset with new asset
+    asset := Asset{
+      ID:             id,
+      Color:          color,
+      Size:           size,
+      Owner:          owner,
+      AppraisedValue: appraisedValue,
+    }
+    assetJSON, err := json.Marshal(asset)
+    if err != nil {
+      return err
     }
 
+    return ctx.GetStub().PutState(id, assetJSON)
+  }
+
 There may be cases where we need the ability to delete an asset from the ledger,
 so let's write a ``DeleteAsset`` function to handle that requirement.
 
 .. code:: go
 
   // DeleteAsset deletes an given asset from the world state.
-     func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
-        exists, err := s.AssetExists(ctx, id)
-        if err != nil {
-          return err
-        }
-        if !exists {
-          return fmt.Errorf("the asset %s does not exist", id)
-        }
+  func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
+    exists, err := s.AssetExists(ctx, id)
+    if err != nil {
+      return err
+    }
+    if !exists {
+      return fmt.Errorf("the asset %s does not exist", id)
+    }
 
-        return ctx.GetStub().DelState(id)
-     }
+    return ctx.GetStub().DelState(id)
+  }
 
 
 We said earlier that is was a good idea to check to see if an asset exists before
@@ -263,34 +270,34 @@ taking an action on it, so let's write a function called ``AssetExists`` to impl
 .. code:: go
 
   // AssetExists returns true when asset with given ID exists in world state
-     func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
-        assetJSON, err := ctx.GetStub().GetState(id)
-        if err != nil {
-          return false, fmt.Errorf("failed to read from world state: %v", err)
-        }
+  func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
+    assetJSON, err := ctx.GetStub().GetState(id)
+    if err != nil {
+      return false, fmt.Errorf("failed to read from world state: %v", err)
+    }
 
-        return assetJSON != nil, nil
-      }
+    return assetJSON != nil, nil
+  }
 
 Next, we'll write a function we'll call ``TransferAsset`` that enables the transfer of an asset from one owner to another.
 
 .. code:: go
 
   // TransferAsset updates the owner field of asset with given id in world state.
-     func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
-        asset, err := s.ReadAsset(ctx, id)
-        if err != nil {
-          return err
-        }
+  func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
+    asset, err := s.ReadAsset(ctx, id)
+    if err != nil {
+      return err
+    }
 
-        asset.Owner = newOwner
-        assetJSON, err := json.Marshal(asset)
-        if err != nil {
-          return err
-        }
+    asset.Owner = newOwner
+    assetJSON, err := json.Marshal(asset)
+    if err != nil {
+      return err
+    }
 
-        return ctx.GetStub().PutState(id, assetJSON)
-      }
+    return ctx.GetStub().PutState(id, assetJSON)
+  }
 
 Let's write a function we'll call ``GetAllAssets`` that enables the querying of the ledger to
 return all of the assets on the ledger.
@@ -298,33 +305,33 @@ return all of the assets on the ledger.
 .. code:: go
 
   // GetAllAssets returns all assets found in world state
-     func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
-  // range query with empty string for startKey and endKey does an
-  // open-ended query of all assets in the chaincode namespace.
-      resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+  func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
+    // range query with empty string for startKey and endKey does an
+    // open-ended query of all assets in the chaincode namespace.
+    resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+    if err != nil {
+      return nil, err
+    }
+    defer resultsIterator.Close()
+
+    var assets []*Asset
+    for resultsIterator.HasNext() {
+      queryResponse, err := resultsIterator.Next()
       if err != nil {
         return nil, err
       }
-      defer resultsIterator.Close()
-
-      var assets []*Asset
-      for resultsIterator.HasNext() {
-        queryResponse, err := resultsIterator.Next()
-        if err != nil {
-          return nil, err
-        }
-
-        var asset Asset
-        err = json.Unmarshal(queryResponse.Value, &asset)
-        if err != nil {
-          return nil, err
-        }
-        assets = append(assets, &asset)
-      }
 
-      return assets, nil
+      var asset Asset
+      err = json.Unmarshal(queryResponse.Value, &asset)
+      if err != nil {
+        return nil, err
+      }
+      assets = append(assets, &asset)
     }
 
+    return assets, nil
+  }
+
 .. _Chaincode Sample:
 
 
@@ -332,7 +339,7 @@ return all of the assets on the ledger.
           to keep this tutorial as clear and straightforward as possible. In a
           real-world implementation, it is likely that packages will be segmented
           where a ``main`` package imports the chaincode package to allow for easy unit testing.
-          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>`__
+          To see what this looks like, see the asset-transfer `Go chaincode <https://github.com/hyperledger/fabric-samples/tree/main/asset-transfer-basic/chaincode-go>`__
           in fabric-samples. If you look at ``assetTransfer.go``, you will see that
           it contains ``package main`` and imports ``package chaincode`` defined in ``smartcontract.go`` and
           located at ``fabric-samples/asset-transfer-basic/chaincode-go/chaincode/``.
@@ -359,193 +366,193 @@ function. Here's the whole chaincode program source.
   )
 
   // SmartContract provides functions for managing an Asset
-     type SmartContract struct {
-        contractapi.Contract
-      }
+  type SmartContract struct {
+    contractapi.Contract
+  }
 
   // Asset describes basic details of what makes up a simple asset
-     type Asset struct {
-        ID             string `json:"ID"`
-        Color          string `json:"color"`
-        Size           int    `json:"size"`
-        Owner          string `json:"owner"`
-        AppraisedValue int    `json:"appraisedValue"`
-      }
+  type Asset struct {
+    ID             string `json:"ID"`
+    Color          string `json:"color"`
+    Size           int    `json:"size"`
+    Owner          string `json:"owner"`
+    AppraisedValue int    `json:"appraisedValue"`
+  }
 
   // InitLedger adds a base set of assets to the ledger
-     func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
-      assets := []Asset{
-        {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
-        {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
-        {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
-        {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
-        {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
-        {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
-      }
-
-      for _, asset := range assets {
-        assetJSON, err := json.Marshal(asset)
-        if err != nil {
-          return err
-        }
-
-        err = ctx.GetStub().PutState(asset.ID, assetJSON)
-        if err != nil {
-          return fmt.Errorf("failed to put to world state. %v", err)
-        }
-      }
-
-      return nil
+  func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+    assets := []Asset{
+      {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
+      {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
+      {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
+      {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
+      {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
+      {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
     }
 
-  // CreateAsset issues a new asset to the world state with given details.
-     func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
-      exists, err := s.AssetExists(ctx, id)
+    for _, asset := range assets {
+      assetJSON, err := json.Marshal(asset)
       if err != nil {
         return err
       }
-      if exists {
-        return fmt.Errorf("the asset %s already exists", id)
-      }
 
-      asset := Asset{
-        ID:             id,
-        Color:          color,
-        Size:           size,
-        Owner:          owner,
-        AppraisedValue: appraisedValue,
-      }
-      assetJSON, err := json.Marshal(asset)
+      err = ctx.GetStub().PutState(asset.ID, assetJSON)
       if err != nil {
-        return err
+        return fmt.Errorf("failed to put to world state. %v", err)
       }
+    }
 
-      return ctx.GetStub().PutState(id, assetJSON)
+    return nil
+  }
+
+  // CreateAsset issues a new asset to the world state with given details.
+  func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+    exists, err := s.AssetExists(ctx, id)
+    if err != nil {
+      return err
+    }
+    if exists {
+      return fmt.Errorf("the asset %s already exists", id)
     }
 
-  // ReadAsset returns the asset stored in the world state with given id.
-     func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
-      assetJSON, err := ctx.GetStub().GetState(id)
-      if err != nil {
-        return nil, fmt.Errorf("failed to read from world state: %v", err)
-      }
-      if assetJSON == nil {
-        return nil, fmt.Errorf("the asset %s does not exist", id)
-      }
+    asset := Asset{
+      ID:             id,
+      Color:          color,
+      Size:           size,
+      Owner:          owner,
+      AppraisedValue: appraisedValue,
+    }
+    assetJSON, err := json.Marshal(asset)
+    if err != nil {
+      return err
+    }
 
-      var asset Asset
-      err = json.Unmarshal(assetJSON, &asset)
-      if err != nil {
-        return nil, err
-      }
+    return ctx.GetStub().PutState(id, assetJSON)
+  }
 
-      return &asset, nil
+  // ReadAsset returns the asset stored in the world state with given id.
+  func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
+    assetJSON, err := ctx.GetStub().GetState(id)
+    if err != nil {
+      return nil, fmt.Errorf("failed to read from world state: %v", err)
+    }
+    if assetJSON == nil {
+      return nil, fmt.Errorf("the asset %s does not exist", id)
     }
 
-  // UpdateAsset updates an existing asset in the world state with provided parameters.
-     func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
-      exists, err := s.AssetExists(ctx, id)
-      if err != nil {
-        return err
-      }
-      if !exists {
-        return fmt.Errorf("the asset %s does not exist", id)
-      }
+    var asset Asset
+    err = json.Unmarshal(assetJSON, &asset)
+    if err != nil {
+      return nil, err
+    }
 
-      // overwriting original asset with new asset
-      asset := Asset{
-        ID:             id,
-        Color:          color,
-        Size:           size,
-        Owner:          owner,
-        AppraisedValue: appraisedValue,
-      }
-      assetJSON, err := json.Marshal(asset)
-      if err != nil {
-        return err
-      }
+    return &asset, nil
+  }
 
-      return ctx.GetStub().PutState(id, assetJSON)
+  // UpdateAsset updates an existing asset in the world state with provided parameters.
+  func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+    exists, err := s.AssetExists(ctx, id)
+    if err != nil {
+      return err
+    }
+    if !exists {
+      return fmt.Errorf("the asset %s does not exist", id)
     }
 
-    // DeleteAsset deletes an given asset from the world state.
-    func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
-      exists, err := s.AssetExists(ctx, id)
-      if err != nil {
-        return err
-      }
-      if !exists {
-        return fmt.Errorf("the asset %s does not exist", id)
-      }
+    // overwriting original asset with new asset
+    asset := Asset{
+      ID:             id,
+      Color:          color,
+      Size:           size,
+      Owner:          owner,
+      AppraisedValue: appraisedValue,
+    }
+    assetJSON, err := json.Marshal(asset)
+    if err != nil {
+      return err
+    }
+
+    return ctx.GetStub().PutState(id, assetJSON)
+  }
 
-      return ctx.GetStub().DelState(id)
+  // DeleteAsset deletes an given asset from the world state.
+  func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
+    exists, err := s.AssetExists(ctx, id)
+    if err != nil {
+      return err
+    }
+    if !exists {
+      return fmt.Errorf("the asset %s does not exist", id)
     }
 
-  // AssetExists returns true when asset with given ID exists in world state
-     func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
-      assetJSON, err := ctx.GetStub().GetState(id)
-      if err != nil {
-        return false, fmt.Errorf("failed to read from world state: %v", err)
-      }
+    return ctx.GetStub().DelState(id)
+  }
 
-      return assetJSON != nil, nil
+  // AssetExists returns true when asset with given ID exists in world state
+  func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
+    assetJSON, err := ctx.GetStub().GetState(id)
+    if err != nil {
+      return false, fmt.Errorf("failed to read from world state: %v", err)
     }
 
-  // TransferAsset updates the owner field of asset with given id in world state.
-     func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
-      asset, err := s.ReadAsset(ctx, id)
-      if err != nil {
-        return err
-      }
+    return assetJSON != nil, nil
+  }
 
-      asset.Owner = newOwner
-      assetJSON, err := json.Marshal(asset)
-      if err != nil {
-        return err
-      }
+  // TransferAsset updates the owner field of asset with given id in world state.
+  func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
+    asset, err := s.ReadAsset(ctx, id)
+    if err != nil {
+      return err
+    }
 
-      return ctx.GetStub().PutState(id, assetJSON)
+    asset.Owner = newOwner
+    assetJSON, err := json.Marshal(asset)
+    if err != nil {
+      return err
     }
 
+    return ctx.GetStub().PutState(id, assetJSON)
+  }
+
   // GetAllAssets returns all assets found in world state
-     func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
-  // range query with empty string for startKey and endKey does an
-  // open-ended query of all assets in the chaincode namespace.
-      resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+  func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
+    // range query with empty string for startKey and endKey does an
+    // open-ended query of all assets in the chaincode namespace.
+    resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+    if err != nil {
+      return nil, err
+    }
+    defer resultsIterator.Close()
+
+    var assets []*Asset
+    for resultsIterator.HasNext() {
+      queryResponse, err := resultsIterator.Next()
       if err != nil {
         return nil, err
       }
-      defer resultsIterator.Close()
-
-      var assets []*Asset
-      for resultsIterator.HasNext() {
-        queryResponse, err := resultsIterator.Next()
-        if err != nil {
-          return nil, err
-        }
-
-        var asset Asset
-        err = json.Unmarshal(queryResponse.Value, &asset)
-        if err != nil {
-          return nil, err
-        }
-        assets = append(assets, &asset)
-      }
 
-      return assets, nil
-    }
-
-    func main() {
-      assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
+      var asset Asset
+      err = json.Unmarshal(queryResponse.Value, &asset)
       if err != nil {
-        log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
+        return nil, err
       }
+      assets = append(assets, &asset)
+    }
 
-      if err := assetChaincode.Start(); err != nil {
-        log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
-      }
+    return assets, nil
+  }
+
+  func main() {
+    assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
+    if err != nil {
+      log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
     }
 
+    if err := assetChaincode.Start(); err != nil {
+      log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
+    }
+  }
+
 Chaincode access control
 ------------------------
 
@@ -586,5 +593,57 @@ Once dependencies are vendored in your chaincode directory, ``peer chaincode pac
 and ``peer chaincode install`` operations will then include code associated with the
 dependencies into the chaincode package.
 
+JSON determinism
+----------------
+Being able to predictably handle data formats is critical, and also the ability to search the data held within the blockchain.
+
+Technical Problem
+^^^^^^^^^^^^^^^^^
+The format of the data that is stored in Fabric is at the discretion of the user. 
+The lowest level API accepts a byte array and stores that - what this represents is not a concern to Fabric.
+The important thing is when simulating transactions, given the same inputs chaincode gives the same byte array.
+Otherwise, the endorsements may not all match and the transaction will either not be submitted or will be invalidated.
+
+JSON is often used as the data format to store data on the ledger, and is required if using CouchDB queries.
+
+JSON though is not a deterministic data format - the order of elements can change,
+whilst still representing the same data semantically. The challenge, therefore, is to be able to generate a consistent set of JSON.
+
+A solution
+^^^^^^^^^^
+Generate a consistent set of ``JSON`` across multiple languages.
+Each language have different features and libraries that you can use to convert an object to JSON.
+The best approach to achieve determinism across different languages is to choose a canonical way as a common guideline to format JSON.
+In order to get a consistent hash across languages you can format JSON in alphabetic order.
+
+Golang
+^^^^^^
+In Golang the ``encoding/json`` package is utilized to serialise a Struct Object into JSON.
+More specifically the ``Marshal`` function is used, the latter marshals maps in sorted key order and keeps structs in the order that the fields are declared.
+Since structs are marshaled in field declaration order, follow alphabetic order when defining a new structure.
+
+Node.js
+^^^^^^^
+In Javascript, when serialising object into JSON, the function ``JSON.stringify()`` is commonly used.
+However, to achieve consistent results, a deterministic version of JSON.stringify() is needed; in this way it is possible to get a consistent hash from stringified results.
+``json-stringify-deterministic`` is a good library to do so and can be used combined with ``sort-keys-recursive`` to attain alphabetic order too. 
+`Here <https://www.npmjs.com/package/json-stringify-deterministic>`_ for a more in-depth tutorial.
+
+Java
+^^^^
+Java provides several libraries to serialize an object into a JSON string. However not all of them provide consistency and ordering.
+The ``Gson`` library, for example, does not provide any consistency and should therefore be avoided for this application. On the other hand,
+the ``Genson`` library is a good fit for our purpose as it produces consistent JSON in alphabetic oreder.
+
+You can find a good exemplification of this practise on the `asset-transfer-basic <https://github.com/hyperledger/fabric-samples/tree/main/asset-transfer-basic>`_ chaincodes.
+
+.. Note:: 
+        This is only one of the many approaches which we think can be effective.
+        When serialising you can utilise various methods to achieve consistency; nevertheless,
+        considering the different characteristics of the programming languages used in Fabric,
+        the alphabetic approach represents an easy and efficient solution to the problem.
+        In conclusion, feel free to employ a different method if it best suites your needs.
+        P.S. Don’t forget to let us know in the comments if you used a different approach.
+
 .. Licensed under Creative Commons Attribution 4.0 International License
    https://creativecommons.org/licenses/by/4.0/

shimos avatar Sep 28 '23 00:09 shimos