Computer Science/Blockchain

[Blockchain] Blueprint for Smart Contract: smartcontract.go

Henry Cho 2024. 8. 30. 05:16
728x90

포스트 난이도: HOO_Senior


# fabric-samples v2.5

If you're venturing into the world of Hyperledger Fabric, understanding the core components of a smart contract is essential. In this post, we’ll break down the key functions within the smartcontract.go file, which will help you not only build your own smart contract but also grasp the basics of chaincode development in Go.

https://github.com/hyperledger/fabric

 

GitHub - hyperledger/fabric: Hyperledger Fabric is an enterprise-grade permissioned distributed ledger framework for developing

Hyperledger Fabric is an enterprise-grade permissioned distributed ledger framework for developing solutions and applications. Its modular and versatile design satisfies a broad range of industry u...

github.com

Chaincode, commonly referred to as smart contracts in the Hyperledger Fabric ecosystem, is a critical piece that encapsulates business logic agreed upon by members of the blockchain network. The smartcontract.go file typically serves as the blueprint for these contracts, providing the necessary functions to interact with the blockchain.

 

Let’s walk through some of the primary functions you might encounter in smartcontract.go:

  • InitLedger
  • CreateAsset
  • ReadAsset
  • UpdateAsset
  • DeleteAsset
  • TransferAsset

# Imports

The package is named chaincode and imports the necessary libraries. The "encoding/json" package is used for converting Go objects to JSON and vice versa, which is essential for Go-based chaincode. The "fmt" package is used for formatting text, such as creating error messages. The contractapi package provides the interfaces and classes needed to define a chaincode smart contract for Hyperledger Fabric.

package chaincode

import (
	"encoding/json"
	"fmt"

	"github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
)

 


# SmartContract struct

The SmartContract struct embeds contractapi.Contract. This struct serves as the main entry point for your chaincode, and all your functions will be associated with it.

type SmartContract struct {
	contractapi.Contract
}

# Asset struct

The Asset data model represents the structure that will be stored in the ledger. Each Asset has several fields, such as ID, Color, Size, Owner, and AppraisedValue. If you want to create your own smart contract, you might define a new struct here that represents the data model for your specific use case. For example, you could define a struct like Henry  with fields such as Is and Handsome.

type Asset struct {
	AppraisedValue int    `json:"AppraisedValue"`
	Color          string `json:"Color"`
	ID             string `json:"ID"`
	Owner          string `json:"Owner"`
	Size           int    `json:"Size"`
}

# InitLedger Function

This function initializes the ledger with a set of default asset entries. It is called when the chaincode is instantiated on the blockchain network. You can customize the assets array with your own data entries to populate the ledger at the start. Make sure to change the fields according to your new data model.

func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
	assets := []Asset{
		{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
		// other assets...
	}

	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 Function

This function allows a new Asset to be created and stored in the ledger. It first checks if an asset with the given ID already exists, and if not, it creates a new one. To customize this function, modify the parameters and asset creation logic according to your custom data model. Ensure that you adjust the parameters in the function signature and the asset creation process accordingly.

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
	}

	return ctx.GetStub().PutState(id, assetJSON)
}

# ReadAsset Function

This function retrieves an Asset from the ledger using its id. To customize this function, modify the return type and unmarshaling logic according to your custom data model.

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
	}

	return &asset, nil
}

 

# UpdateAsset Function

This function updates an existing Asset in the ledger with new details. It checks if the asset exists before performing the update. To custermize this function, adjust the parameters and the Asset creation logic according to your custom data model.

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)
	}

	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 Function

This function deletes an Asset from the ledger using its id. The logic for deletion is straightforward, but if your smart contract needs specific deletion logic (e.g., transferring data before deletion), you may need to modify it accordingly.

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 Function

This helper function checks whether an Asset exists in the ledger using its id. You might not need to change this unless your existence check requires more complex logic or additional criteria.

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
}

# TransferAsset Function

This function transfers ownership of an Asset to a new owner. It updates the Owner field in the asset and returns the previous owner's name. Adjust the logic if your smart contract requires more complex transfer processes (e.g., multiple ownerships, conditional transfers).

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 Function

This function retrieves all Assets stored in the ledger. To modify this, it needs to return a different data structure or add filters or conditions to the data retrieval process.

func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
	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
		}

		var asset Asset
		err = json.Unmarshal(queryResponse.Value, &asset)
		if err != nil {
			return nil, err
		}
		assets = append(assets, &asset)
	}

	return assets, nil
}

 


 

728x90