Add Tests to FabCar Go Chaincode

Many users raise questions on how to test chaincode. It used
to be much easier with the old shim, as they could directly
use the old mock stub in the shim. Now that it no longer exists
the fabcar example can provide an example of how to test chaincode.

Also worth noting is our current directory structure of our Go
chaincodes prevents the creation of mocks due to import cycles.
This change also pushes the chaincode logic down into a `contract`
package.

Signed-off-by: Brett Logan <brett.t.logan@ibm.com>
This commit is contained in:
Brett Logan 2020-07-06 14:54:17 -04:00
parent 683ee8b347
commit 23dc4e698a
8 changed files with 3574 additions and 124 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
// Code generated by counterfeiter. DO NOT EDIT.
package mocks
import (
"sync"
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
"github.com/hyperledger/fabric-samples/chaincode/fabcar/go/contract"
)
type StateQueryIterator struct {
CloseStub func() error
closeMutex sync.RWMutex
closeArgsForCall []struct {
}
closeReturns struct {
result1 error
}
closeReturnsOnCall map[int]struct {
result1 error
}
HasNextStub func() bool
hasNextMutex sync.RWMutex
hasNextArgsForCall []struct {
}
hasNextReturns struct {
result1 bool
}
hasNextReturnsOnCall map[int]struct {
result1 bool
}
NextStub func() (*queryresult.KV, error)
nextMutex sync.RWMutex
nextArgsForCall []struct {
}
nextReturns struct {
result1 *queryresult.KV
result2 error
}
nextReturnsOnCall map[int]struct {
result1 *queryresult.KV
result2 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *StateQueryIterator) Close() error {
fake.closeMutex.Lock()
ret, specificReturn := fake.closeReturnsOnCall[len(fake.closeArgsForCall)]
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
}{})
fake.recordInvocation("Close", []interface{}{})
fake.closeMutex.Unlock()
if fake.CloseStub != nil {
return fake.CloseStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.closeReturns
return fakeReturns.result1
}
func (fake *StateQueryIterator) CloseCallCount() int {
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
return len(fake.closeArgsForCall)
}
func (fake *StateQueryIterator) CloseCalls(stub func() error) {
fake.closeMutex.Lock()
defer fake.closeMutex.Unlock()
fake.CloseStub = stub
}
func (fake *StateQueryIterator) CloseReturns(result1 error) {
fake.closeMutex.Lock()
defer fake.closeMutex.Unlock()
fake.CloseStub = nil
fake.closeReturns = struct {
result1 error
}{result1}
}
func (fake *StateQueryIterator) CloseReturnsOnCall(i int, result1 error) {
fake.closeMutex.Lock()
defer fake.closeMutex.Unlock()
fake.CloseStub = nil
if fake.closeReturnsOnCall == nil {
fake.closeReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.closeReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *StateQueryIterator) HasNext() bool {
fake.hasNextMutex.Lock()
ret, specificReturn := fake.hasNextReturnsOnCall[len(fake.hasNextArgsForCall)]
fake.hasNextArgsForCall = append(fake.hasNextArgsForCall, struct {
}{})
fake.recordInvocation("HasNext", []interface{}{})
fake.hasNextMutex.Unlock()
if fake.HasNextStub != nil {
return fake.HasNextStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.hasNextReturns
return fakeReturns.result1
}
func (fake *StateQueryIterator) HasNextCallCount() int {
fake.hasNextMutex.RLock()
defer fake.hasNextMutex.RUnlock()
return len(fake.hasNextArgsForCall)
}
func (fake *StateQueryIterator) HasNextCalls(stub func() bool) {
fake.hasNextMutex.Lock()
defer fake.hasNextMutex.Unlock()
fake.HasNextStub = stub
}
func (fake *StateQueryIterator) HasNextReturns(result1 bool) {
fake.hasNextMutex.Lock()
defer fake.hasNextMutex.Unlock()
fake.HasNextStub = nil
fake.hasNextReturns = struct {
result1 bool
}{result1}
}
func (fake *StateQueryIterator) HasNextReturnsOnCall(i int, result1 bool) {
fake.hasNextMutex.Lock()
defer fake.hasNextMutex.Unlock()
fake.HasNextStub = nil
if fake.hasNextReturnsOnCall == nil {
fake.hasNextReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.hasNextReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *StateQueryIterator) Next() (*queryresult.KV, error) {
fake.nextMutex.Lock()
ret, specificReturn := fake.nextReturnsOnCall[len(fake.nextArgsForCall)]
fake.nextArgsForCall = append(fake.nextArgsForCall, struct {
}{})
fake.recordInvocation("Next", []interface{}{})
fake.nextMutex.Unlock()
if fake.NextStub != nil {
return fake.NextStub()
}
if specificReturn {
return ret.result1, ret.result2
}
fakeReturns := fake.nextReturns
return fakeReturns.result1, fakeReturns.result2
}
func (fake *StateQueryIterator) NextCallCount() int {
fake.nextMutex.RLock()
defer fake.nextMutex.RUnlock()
return len(fake.nextArgsForCall)
}
func (fake *StateQueryIterator) NextCalls(stub func() (*queryresult.KV, error)) {
fake.nextMutex.Lock()
defer fake.nextMutex.Unlock()
fake.NextStub = stub
}
func (fake *StateQueryIterator) NextReturns(result1 *queryresult.KV, result2 error) {
fake.nextMutex.Lock()
defer fake.nextMutex.Unlock()
fake.NextStub = nil
fake.nextReturns = struct {
result1 *queryresult.KV
result2 error
}{result1, result2}
}
func (fake *StateQueryIterator) NextReturnsOnCall(i int, result1 *queryresult.KV, result2 error) {
fake.nextMutex.Lock()
defer fake.nextMutex.Unlock()
fake.NextStub = nil
if fake.nextReturnsOnCall == nil {
fake.nextReturnsOnCall = make(map[int]struct {
result1 *queryresult.KV
result2 error
})
}
fake.nextReturnsOnCall[i] = struct {
result1 *queryresult.KV
result2 error
}{result1, result2}
}
func (fake *StateQueryIterator) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.hasNextMutex.RLock()
defer fake.hasNextMutex.RUnlock()
fake.nextMutex.RLock()
defer fake.nextMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *StateQueryIterator) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ contract.StateQueryIterator = new(StateQueryIterator)

View file

@ -0,0 +1,167 @@
// Code generated by counterfeiter. DO NOT EDIT.
package mocks
import (
"sync"
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-samples/chaincode/fabcar/go/contract"
)
type TransactionContext struct {
GetClientIdentityStub func() cid.ClientIdentity
getClientIdentityMutex sync.RWMutex
getClientIdentityArgsForCall []struct {
}
getClientIdentityReturns struct {
result1 cid.ClientIdentity
}
getClientIdentityReturnsOnCall map[int]struct {
result1 cid.ClientIdentity
}
GetStubStub func() shim.ChaincodeStubInterface
getStubMutex sync.RWMutex
getStubArgsForCall []struct {
}
getStubReturns struct {
result1 shim.ChaincodeStubInterface
}
getStubReturnsOnCall map[int]struct {
result1 shim.ChaincodeStubInterface
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *TransactionContext) GetClientIdentity() cid.ClientIdentity {
fake.getClientIdentityMutex.Lock()
ret, specificReturn := fake.getClientIdentityReturnsOnCall[len(fake.getClientIdentityArgsForCall)]
fake.getClientIdentityArgsForCall = append(fake.getClientIdentityArgsForCall, struct {
}{})
fake.recordInvocation("GetClientIdentity", []interface{}{})
fake.getClientIdentityMutex.Unlock()
if fake.GetClientIdentityStub != nil {
return fake.GetClientIdentityStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.getClientIdentityReturns
return fakeReturns.result1
}
func (fake *TransactionContext) GetClientIdentityCallCount() int {
fake.getClientIdentityMutex.RLock()
defer fake.getClientIdentityMutex.RUnlock()
return len(fake.getClientIdentityArgsForCall)
}
func (fake *TransactionContext) GetClientIdentityCalls(stub func() cid.ClientIdentity) {
fake.getClientIdentityMutex.Lock()
defer fake.getClientIdentityMutex.Unlock()
fake.GetClientIdentityStub = stub
}
func (fake *TransactionContext) GetClientIdentityReturns(result1 cid.ClientIdentity) {
fake.getClientIdentityMutex.Lock()
defer fake.getClientIdentityMutex.Unlock()
fake.GetClientIdentityStub = nil
fake.getClientIdentityReturns = struct {
result1 cid.ClientIdentity
}{result1}
}
func (fake *TransactionContext) GetClientIdentityReturnsOnCall(i int, result1 cid.ClientIdentity) {
fake.getClientIdentityMutex.Lock()
defer fake.getClientIdentityMutex.Unlock()
fake.GetClientIdentityStub = nil
if fake.getClientIdentityReturnsOnCall == nil {
fake.getClientIdentityReturnsOnCall = make(map[int]struct {
result1 cid.ClientIdentity
})
}
fake.getClientIdentityReturnsOnCall[i] = struct {
result1 cid.ClientIdentity
}{result1}
}
func (fake *TransactionContext) GetStub() shim.ChaincodeStubInterface {
fake.getStubMutex.Lock()
ret, specificReturn := fake.getStubReturnsOnCall[len(fake.getStubArgsForCall)]
fake.getStubArgsForCall = append(fake.getStubArgsForCall, struct {
}{})
fake.recordInvocation("GetStub", []interface{}{})
fake.getStubMutex.Unlock()
if fake.GetStubStub != nil {
return fake.GetStubStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.getStubReturns
return fakeReturns.result1
}
func (fake *TransactionContext) GetStubCallCount() int {
fake.getStubMutex.RLock()
defer fake.getStubMutex.RUnlock()
return len(fake.getStubArgsForCall)
}
func (fake *TransactionContext) GetStubCalls(stub func() shim.ChaincodeStubInterface) {
fake.getStubMutex.Lock()
defer fake.getStubMutex.Unlock()
fake.GetStubStub = stub
}
func (fake *TransactionContext) GetStubReturns(result1 shim.ChaincodeStubInterface) {
fake.getStubMutex.Lock()
defer fake.getStubMutex.Unlock()
fake.GetStubStub = nil
fake.getStubReturns = struct {
result1 shim.ChaincodeStubInterface
}{result1}
}
func (fake *TransactionContext) GetStubReturnsOnCall(i int, result1 shim.ChaincodeStubInterface) {
fake.getStubMutex.Lock()
defer fake.getStubMutex.Unlock()
fake.GetStubStub = nil
if fake.getStubReturnsOnCall == nil {
fake.getStubReturnsOnCall = make(map[int]struct {
result1 shim.ChaincodeStubInterface
})
}
fake.getStubReturnsOnCall[i] = struct {
result1 shim.ChaincodeStubInterface
}{result1}
}
func (fake *TransactionContext) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.getClientIdentityMutex.RLock()
defer fake.getClientIdentityMutex.RUnlock()
fake.getStubMutex.RLock()
defer fake.getStubMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *TransactionContext) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ contract.TransactionContext = new(TransactionContext)

View file

@ -0,0 +1,21 @@
package contract
import (
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
//go:generate counterfeiter -o mocks/transaction.go -fake-name TransactionContext . TransactionContext
type TransactionContext interface {
contractapi.TransactionContextInterface
}
//go:generate counterfeiter -o mocks/chaincodestub.go -fake-name ChaincodeStub . ChaincodeStub
type ChaincodeStub interface {
shim.ChaincodeStubInterface
}
//go:generate counterfeiter -o mocks/statequeryiterator.go -fake-name StateQueryIterator . StateQueryIterator
type StateQueryIterator interface {
shim.StateQueryIteratorInterface
}

View file

@ -0,0 +1,127 @@
package contract
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// SmartContract provides functions for managing a car
type SmartContract struct {
contractapi.Contract
}
// Car describes basic details of what makes up a car
type Car struct {
Make string `json:"make"`
Model string `json:"model"`
Colour string `json:"colour"`
Owner string `json:"owner"`
}
// QueryResult structure used for handling result of query
type QueryResult struct {
Key string `json:"Key"`
Record *Car
}
// InitLedger adds a base set of cars to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
cars := []Car{
{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
}
for i, car := range cars {
carAsBytes, _ := json.Marshal(car)
key := fmt.Sprintf("CAR%d", i)
err := ctx.GetStub().PutState(key, carAsBytes)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}
return nil
}
// CreateCar adds a new car to the world state with given details
func (s *SmartContract) CreateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string) error {
car := Car{
Make: make,
Model: model,
Colour: colour,
Owner: owner,
}
carAsBytes, err := json.Marshal(car)
if err != nil {
return fmt.Errorf("failed marshalling to json: %v", err)
}
return ctx.GetStub().PutState(carNumber, carAsBytes)
}
// QueryCar returns the car stored in the world state with given id
func (s *SmartContract) QueryCar(ctx contractapi.TransactionContextInterface, carNumber string) (*Car, error) {
carAsBytes, err := ctx.GetStub().GetState(carNumber)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if carAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", carNumber)
}
var car Car
err = json.Unmarshal(carAsBytes, &car)
if err != nil {
return nil, err
}
return &car, nil
}
// QueryAllCars returns all cars found in world state
func (s *SmartContract) QueryAllCars(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
// Return all cars by using empty startKey and endKey
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 car *Car
err = json.Unmarshal(queryResponse.Value, &car)
if err != nil {
return nil, fmt.Errorf("failed marshalling to json: %v", err)
}
queryResult := QueryResult{Key: queryResponse.Key, Record: car}
results = append(results, queryResult)
}
return results, nil
}
// ChangeCarOwner updates the owner field of car with given id in world state
func (s *SmartContract) ChangeCarOwner(ctx contractapi.TransactionContextInterface, carNumber string, newOwner string) error {
car, err := s.QueryCar(ctx, carNumber)
if err != nil {
return err
}
car.Owner = newOwner
carAsBytes, err := json.Marshal(car)
if err != nil {
return fmt.Errorf("failed marshalling to json: %v", err)
}
return ctx.GetStub().PutState(carNumber, carAsBytes)
}

View file

@ -0,0 +1,133 @@
package contract_test
import (
"encoding/json"
"fmt"
"testing"
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
"github.com/hyperledger/fabric-samples/chaincode/fabcar/go/contract"
"github.com/hyperledger/fabric-samples/chaincode/fabcar/go/contract/mocks"
"github.com/stretchr/testify/require"
)
func TestInitLedger(t *testing.T) {
chaincodeStub := &mocks.ChaincodeStub{}
transactionContext := &mocks.TransactionContext{}
transactionContext.GetStubReturns(chaincodeStub)
fabcar := &contract.SmartContract{}
err := fabcar.InitLedger(transactionContext)
require.NoError(t, err)
chaincodeStub.PutStateReturns(fmt.Errorf("failed inserting key"))
err = fabcar.InitLedger(transactionContext)
require.EqualError(t, err, "failed to put to world state. failed inserting key")
}
func TestCreateCar(t *testing.T) {
chaincodeStub := &mocks.ChaincodeStub{}
transactionContext := &mocks.TransactionContext{}
transactionContext.GetStubReturns(chaincodeStub)
fabcar := &contract.SmartContract{}
err := fabcar.CreateCar(transactionContext, "CAR1", "Ford", "F150", "red", "Tim")
require.NoError(t, err)
chaincodeStub.PutStateReturns(fmt.Errorf("failed inserting key"))
err = fabcar.CreateCar(transactionContext, "CAR1", "Ford", "F150", "red", "Tim")
require.EqualError(t, err, "failed inserting key")
}
func TestQueryCar(t *testing.T) {
car := &contract.Car{
Make: "Ford",
Model: "F150",
Colour: "red",
Owner: "Tim",
}
bytes, err := json.Marshal(car)
require.NoError(t, err)
chaincodeStub := &mocks.ChaincodeStub{}
chaincodeStub.GetStateReturns(bytes, nil)
transactionContext := &mocks.TransactionContext{}
transactionContext.GetStubReturns(chaincodeStub)
fabcar := &contract.SmartContract{}
car, err = fabcar.QueryCar(transactionContext, "CAR1")
require.NoError(t, err)
require.NotNil(t, car)
chaincodeStub.GetStateReturns(nil, nil)
car, err = fabcar.QueryCar(transactionContext, "CAR1")
require.EqualError(t, err, "CAR1 does not exist")
require.Nil(t, car)
chaincodeStub.GetStateReturns([]byte{}, fmt.Errorf("failed retrieving key"))
car, err = fabcar.QueryCar(transactionContext, "CAR1")
require.Nil(t, car)
require.EqualError(t, err, "failed to read from world state: failed retrieving key")
}
func TestQueryAllCars(t *testing.T) {
car := &contract.Car{
Make: "Ford",
Model: "F150",
Colour: "red",
Owner: "Tim",
}
bytes, err := json.Marshal(car)
require.NoError(t, err)
iterator := &mocks.StateQueryIterator{}
iterator.HasNextReturnsOnCall(0, true)
iterator.HasNextReturnsOnCall(1, false)
iterator.NextReturns(&queryresult.KV{Value: bytes}, nil)
chaincodeStub := &mocks.ChaincodeStub{}
chaincodeStub.GetStateByRangeReturns(iterator, nil)
transactionContext := &mocks.TransactionContext{}
transactionContext.GetStubReturns(chaincodeStub)
fabcar := &contract.SmartContract{}
cars, err := fabcar.QueryAllCars(transactionContext)
require.NoError(t, err)
require.NotNil(t, cars)
iterator.HasNextReturns(true)
iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
cars, err = fabcar.QueryAllCars(transactionContext)
require.EqualError(t, err, "failed retrieving next item")
require.Nil(t, cars)
chaincodeStub.GetStateByRangeReturns(nil, fmt.Errorf("failed retrieving all cars"))
cars, err = fabcar.QueryAllCars(transactionContext)
require.EqualError(t, err, "failed retrieving all cars")
require.Nil(t, cars)
}
func TestChangeCarOwner(t *testing.T) {
car := &contract.Car{
Make: "Ford",
Model: "F150",
Colour: "red",
Owner: "Tim",
}
bytes, err := json.Marshal(car)
require.NoError(t, err)
chaincodeStub := &mocks.ChaincodeStub{}
chaincodeStub.GetStateReturns(bytes, nil)
transactionContext := &mocks.TransactionContext{}
transactionContext.GetStubReturns(chaincodeStub)
fabcar := &contract.SmartContract{}
err = fabcar.ChangeCarOwner(transactionContext, "CAR1", "Ben")
require.NoError(t, err)
chaincodeStub.GetStateReturns(nil, nil)
err = fabcar.ChangeCarOwner(transactionContext, "CAR1", "Ben")
require.EqualError(t, err, "CAR1 does not exist")
}

View file

@ -5,135 +5,14 @@ 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/chaincode/fabcar/go/contract"
)
// SmartContract provides functions for managing a car
type SmartContract struct {
contractapi.Contract
}
// Car describes basic details of what makes up a car
type Car struct {
Make string `json:"make"`
Model string `json:"model"`
Colour string `json:"colour"`
Owner string `json:"owner"`
}
// QueryResult structure used for handling result of query
type QueryResult struct {
Key string `json:"Key"`
Record *Car
}
// InitLedger adds a base set of cars to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
cars := []Car{
{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
}
for i, car := range cars {
carAsBytes, _ := json.Marshal(car)
key := fmt.Sprintf("CAR%d", i)
err := ctx.GetStub().PutState(key, carAsBytes)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}
return nil
}
// CreateCar adds a new car to the world state with given details
func (s *SmartContract) CreateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string) error {
car := Car{
Make: make,
Model: model,
Colour: colour,
Owner: owner,
}
carAsBytes, err := json.Marshal(car)
if err != nil {
return fmt.Errorf("failed marshalling to json: %v", err)
}
return ctx.GetStub().PutState(carNumber, carAsBytes)
}
// QueryCar returns the car stored in the world state with given id
func (s *SmartContract) QueryCar(ctx contractapi.TransactionContextInterface, carNumber string) (*Car, error) {
carAsBytes, err := ctx.GetStub().GetState(carNumber)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if carAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", carNumber)
}
var car *Car
err = json.Unmarshal(carAsBytes, car)
if err != nil {
return nil, err
}
return car, nil
}
// QueryAllCars returns all cars found in world state
func (s *SmartContract) QueryAllCars(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
// Return all cars by using empty startKey and endKey
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 car *Car
err = json.Unmarshal(queryResponse.Value, car)
if err != nil {
return nil, fmt.Errorf("failed marshalling to json: %v", err)
}
queryResult := QueryResult{Key: queryResponse.Key, Record: car}
results = append(results, queryResult)
}
return results, nil
}
// ChangeCarOwner updates the owner field of car with given id in world state
func (s *SmartContract) ChangeCarOwner(ctx contractapi.TransactionContextInterface, carNumber string, newOwner string) error {
car, err := s.QueryCar(ctx, carNumber)
if err != nil {
return err
}
car.Owner = newOwner
carAsBytes, err := json.Marshal(car)
if err != nil {
return fmt.Errorf("failed marshalling to json: %v", err)
}
return ctx.GetStub().PutState(carNumber, carAsBytes)
}
func main() {
chaincode, err := contractapi.NewChaincode(&SmartContract{})
chaincode, err := contractapi.NewChaincode(&contract.SmartContract{})
if err != nil {
log.Panicf("Error create fabcar chaincode: %v", err)
}

View file

@ -2,4 +2,11 @@ module github.com/hyperledger/fabric-samples/chaincode/fabcar/go
go 1.13
require github.com/hyperledger/fabric-contract-api-go v1.1.0
require (
github.com/hyperledger/fabric-contract-api-go v1.1.0
github.com/golang/protobuf v1.3.2
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212
github.com/hyperledger/fabric-contract-api-go v1.1.0
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e
github.com/stretchr/testify v1.5.1
)