fabric-samples/asset-transfer-basic/chaincode-external/assetTransfer.go
Mark S. Lewis 63cc77bdc3
Avoid usage of deprecated ioutil Go package (#843)
* Refactor Go files
1. replace deprecated ioutil functions (ioutil is deprecated since Go 1.16)
2. fix variable names that collide with imported package name
3. fix typos

Also update Go version specified by Go modules to ensure a Go version is used in which ioutil is deprecated and replacement functions are available in os and io packages.

Signed-off-by: Tommy TIAN <xtianae@connect.ust.hk>
Co-authored-by: Mark S. Lewis <mark_lewis@uk.ibm.com>

* Specify go 1.18 instead of go 1.19 in go.mod files

Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>

Signed-off-by: Tommy TIAN <xtianae@connect.ust.hk>
Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
Co-authored-by: Tommy TIAN <xtianae@connect.ust.hk>
2022-10-25 07:04:39 -04:00

297 lines
7.6 KiB
Go

/*
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"strconv"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type serverConfig struct {
CCID string
Address string
}
// SmartContract provides functions for managing an asset
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"`
}
// QueryResult structure used for handling result of query
type QueryResult struct {
Key string `json:"Key"`
Record *Asset
}
// InitLedger adds a base set of cars 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
}
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, 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
}
return ctx.GetStub().PutState(id, assetJSON)
}
// 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. %s", err.Error())
}
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
}
return &asset, nil
}
// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id, 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
}
return ctx.GetStub().PutState(id, assetJSON)
}
// DeleteAsset deletes a 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)
}
return ctx.GetStub().DelState(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. %s", err.Error())
}
return assetJSON != nil, nil
}
// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return "", err
}
oldOwner := asset.Owner
asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return "", err
}
err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
}
// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]QueryResult, 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 results []QueryResult
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
}
queryResult := QueryResult{Key: queryResponse.Key, Record: &asset}
results = append(results, queryResult)
}
return results, nil
}
func main() {
// See chaincode.env.example
config := serverConfig{
CCID: os.Getenv("CHAINCODE_ID"),
Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"),
}
chaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
log.Panicf("error create asset-transfer-basic chaincode: %s", err)
}
server := &shim.ChaincodeServer{
CCID: config.CCID,
Address: config.Address,
CC: chaincode,
TLSProps: getTLSProperties(),
}
if err := server.Start(); err != nil {
log.Panicf("error starting asset-transfer-basic chaincode: %s", err)
}
}
func getTLSProperties() shim.TLSProperties {
// Check if chaincode is TLS enabled
tlsDisabledStr := getEnvOrDefault("CHAINCODE_TLS_DISABLED", "true")
key := getEnvOrDefault("CHAINCODE_TLS_KEY", "")
cert := getEnvOrDefault("CHAINCODE_TLS_CERT", "")
clientCACert := getEnvOrDefault("CHAINCODE_CLIENT_CA_CERT", "")
// convert tlsDisabledStr to boolean
tlsDisabled := getBoolOrDefault(tlsDisabledStr, false)
var keyBytes, certBytes, clientCACertBytes []byte
var err error
if !tlsDisabled {
keyBytes, err = os.ReadFile(key)
if err != nil {
log.Panicf("error while reading the crypto file: %s", err)
}
certBytes, err = os.ReadFile(cert)
if err != nil {
log.Panicf("error while reading the crypto file: %s", err)
}
}
// Did not request for the peer cert verification
if clientCACert != "" {
clientCACertBytes, err = os.ReadFile(clientCACert)
if err != nil {
log.Panicf("error while reading the crypto file: %s", err)
}
}
return shim.TLSProperties{
Disabled: tlsDisabled,
Key: keyBytes,
Cert: certBytes,
ClientCACerts: clientCACertBytes,
}
}
func getEnvOrDefault(env, defaultVal string) string {
value, ok := os.LookupEnv(env)
if !ok {
value = defaultVal
}
return value
}
// Note that the method returns default value if the string
// cannot be parsed!
func getBoolOrDefault(value string, defaultVal bool) bool {
parsed, err := strconv.ParseBool(value)
if err != nil {
return defaultVal
}
return parsed
}