mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
Refactor Chaincode Into Idiomatic Go
Cleans up the code and pushes the smart contract implementation down into a `chaincode` package. This is in prep for implementing unit testing in the chaincode, as you cannot mock code that is part of the main package. Signed-off-by: Brett Logan <brett.t.logan@ibm.com>
This commit is contained in:
parent
5fe1387a89
commit
a04db374aa
2 changed files with 199 additions and 199 deletions
|
|
@ -5,212 +5,19 @@ SPDX-License-Identifier: Apache-2.0
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
|
||||
)
|
||||
|
||||
// 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 assets to the ledger
|
||||
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
|
||||
assets := []Asset{
|
||||
Asset{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
|
||||
Asset{ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
|
||||
Asset{ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
|
||||
Asset{ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
|
||||
Asset{ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
|
||||
Asset{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. %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateAsset issues a new asset to the world state with given details.
|
||||
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, color, owner string, size, 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)
|
||||
}
|
||||
|
||||
asset := new(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, owner string, size, 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)
|
||||
}
|
||||
|
||||
// overwritting 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 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)
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
return ctx.GetStub().PutState(id, assetJSON)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
results := []QueryResult{}
|
||||
|
||||
for resultsIterator.HasNext() {
|
||||
queryResponse, err := resultsIterator.Next()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
asset := new(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() {
|
||||
|
||||
chaincode, err := contractapi.NewChaincode(new(SmartContract))
|
||||
|
||||
assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
|
||||
if err != nil {
|
||||
fmt.Printf("Error create asset-transfer-basic chaincode: %s", err.Error())
|
||||
return
|
||||
log.Panicf("Error create asset-transfer-basic chaincode: %v", err)
|
||||
}
|
||||
|
||||
if err := chaincode.Start(); err != nil {
|
||||
fmt.Printf("Error starting asset-transfer-basic chaincode: %s", err.Error())
|
||||
if err := assetChaincode.Start(); err != nil {
|
||||
log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
193
asset-transfer-basic/chaincode-go/chaincode/smartcontract.go
Normal file
193
asset-transfer-basic/chaincode-go/chaincode/smartcontract.go
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
package chaincode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
)
|
||||
|
||||
// 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 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
|
||||
}
|
||||
|
||||
// CreateAsset issues a new asset to the world state with given details.
|
||||
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, color, owner string, size, 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: %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 updates an existing asset in the world state with provided parameters.
|
||||
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id, color, owner string, size, 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 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)
|
||||
}
|
||||
|
||||
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: %v", err)
|
||||
}
|
||||
|
||||
return assetJSON != nil, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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) ([]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
|
||||
}
|
||||
Loading…
Reference in a new issue