fabric-samples/full-stack-asset-transfer-guide/contracts/asset-transfer-go/smartcontract/asset_transfer.go
vishal c4c4a2589e Add Go implementation of full-stack asset transfer smart contract
Signed-off-by: vishal <httpsvishal07@gmail.com>
2026-06-04 21:27:45 +05:30

258 lines
6.2 KiB
Go

/*
SPDX-License-Identifier: Apache-2.0
*/
package smartcontract
import (
"encoding/json"
"fmt"
"log"
"github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, asset *Asset) error {
if asset == nil {
return fmt.Errorf("asset cannot be nil")
}
if asset.ID == "" {
return fmt.Errorf("Missing ID")
}
ownerIdentifier, err := clientIdentifierWithUser(ctx, asset.Owner)
if err != nil {
return err
}
asset.Owner, err = marshalOwnerIdentifier(ownerIdentifier)
if err != nil {
return err
}
exists, err := s.AssetExists(ctx, asset.ID)
if err != nil {
return err
}
if exists {
return fmt.Errorf("The asset %s already exists", asset.ID)
}
assetBytes, err := json.Marshal(asset)
if err != nil {
return fmt.Errorf("failed to marshal asset: %v", err)
}
if err := ctx.GetStub().PutState(asset.ID, assetBytes); err != nil {
return fmt.Errorf("failed to put asset in world state: %v", err)
}
clientMSP, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSP ID: %v", err)
}
if err := setStateBasedEndorsement(ctx, asset.ID, clientMSP); err != nil {
return err
}
if err := ctx.GetStub().SetEvent("CreateAsset", assetBytes); err != nil {
return fmt.Errorf("failed to set event CreateAsset: %v", err)
}
return nil
}
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
assetBytes, err := s.readAsset(ctx, id)
if err != nil {
return nil, err
}
var asset Asset
if err := json.Unmarshal(assetBytes, &asset); err != nil {
return nil, fmt.Errorf("failed to unmarshal asset: %v", err)
}
return &asset, nil
}
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, assetUpdate *Asset) error {
if assetUpdate == nil {
return fmt.Errorf("asset cannot be nil")
}
if assetUpdate.ID == "" {
return fmt.Errorf("No asset ID specified")
}
existingAsset, err := s.ReadAsset(ctx, assetUpdate.ID)
if err != nil {
return err
}
allowed, err := hasWritePermission(ctx, existingAsset)
if err != nil {
return err
}
if !allowed {
return fmt.Errorf("Only owner can update assets")
}
updatedAsset := *existingAsset
if assetUpdate.Color != "" {
updatedAsset.Color = assetUpdate.Color
}
if assetUpdate.Size != 0 {
updatedAsset.Size = assetUpdate.Size
}
if assetUpdate.AppraisedValue != 0 {
updatedAsset.AppraisedValue = assetUpdate.AppraisedValue
}
assetBytes, err := json.Marshal(&updatedAsset)
if err != nil {
return fmt.Errorf("failed to marshal updated asset: %v", err)
}
if err := ctx.GetStub().PutState(updatedAsset.ID, assetBytes); err != nil {
return fmt.Errorf("failed to put asset in world state: %v", err)
}
clientMSP, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSP ID: %v", err)
}
if err := setStateBasedEndorsement(ctx, updatedAsset.ID, clientMSP); err != nil {
return err
}
if err := ctx.GetStub().SetEvent("UpdateAsset", assetBytes); err != nil {
return fmt.Errorf("failed to set event UpdateAsset: %v", err)
}
return nil
}
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return err
}
allowed, err := hasWritePermission(ctx, asset)
if err != nil {
return err
}
if !allowed {
return fmt.Errorf("Only owner can delete assets")
}
if err := ctx.GetStub().DelState(id); err != nil {
return fmt.Errorf("failed to delete asset from world state: %v", err)
}
assetBytes, err := json.Marshal(asset)
if err != nil {
return fmt.Errorf("failed to marshal asset event payload: %v", err)
}
if err := ctx.GetStub().SetEvent("DeleteAsset", assetBytes); err != nil {
return fmt.Errorf("failed to set event DeleteAsset: %v", err)
}
return nil
}
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
assetBytes, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return len(assetBytes) > 0, nil
}
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string, newOwnerOrg string) error {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return err
}
allowed, err := hasWritePermission(ctx, asset)
if err != nil {
return err
}
if !allowed {
return fmt.Errorf("Only owner can transfer assets")
}
asset.Owner, err = marshalOwnerIdentifier(ownerIdentifier(newOwner, newOwnerOrg))
if err != nil {
return err
}
assetBytes, err := json.Marshal(asset)
if err != nil {
return fmt.Errorf("failed to marshal transferred asset: %v", err)
}
if err := ctx.GetStub().PutState(id, assetBytes); err != nil {
return fmt.Errorf("failed to put asset in world state: %v", err)
}
if err := setStateBasedEndorsement(ctx, id, newOwnerOrg); err != nil {
return err
}
if err := ctx.GetStub().SetEvent("TransferAsset", assetBytes); err != nil {
return fmt.Errorf("failed to set event TransferAsset: %v", err)
}
return nil
}
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
iterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, fmt.Errorf("failed to retrieve all assets: %v", err)
}
defer func() {
_ = iterator.Close()
}()
assets := make([]*Asset, 0)
for iterator.HasNext() {
queryResponse, err := iterator.Next()
if err != nil {
return nil, fmt.Errorf("failed retrieving next item: %v", err)
}
var asset Asset
if err := json.Unmarshal(queryResponse.Value, &asset); err != nil {
log.Printf("failed to unmarshal asset from world state: %v", err)
continue
}
assets = append(assets, &asset)
}
return assets, nil
}
func (s *SmartContract) readAsset(ctx contractapi.TransactionContextInterface, id string) ([]byte, error) {
assetBytes, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if len(assetBytes) == 0 {
return nil, fmt.Errorf("Sorry, asset %s has not been created", id)
}
return assetBytes, nil
}