-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add fabcar external service sample (#136)
Add sample which can be used with the builder scripts described in the "Chaincode as an external service" documentation Signed-off-by: James Taylor <jamest@uk.ibm.com>
- Loading branch information
Showing
9 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
chaincode.env* | ||
*.json | ||
*.md | ||
*.tar.gz | ||
*.tgz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
chaincode.env | ||
connection.json | ||
*.tar.gz | ||
*.tgz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Copyright IBM Corp. All Rights Reserved. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
ARG GO_VER=1.13.8 | ||
ARG ALPINE_VER=3.10 | ||
|
||
FROM golang:${GO_VER}-alpine${ALPINE_VER} | ||
|
||
WORKDIR /go/src/github.com/hyperledger/fabric-samples/chaincode/fabcar/external | ||
COPY . . | ||
|
||
RUN go get -d -v ./... | ||
RUN go install -v ./... | ||
|
||
EXPOSE 9999 | ||
CMD ["external"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# FabCar as an external service | ||
|
||
See the "Chaincode as an external service" documentation for running chaincode as an external service. | ||
This includes details of the external builder and launcher scripts which will peers in your Fabric network will require. | ||
|
||
The FabCar chaincode requires two environment variables to run, `CHAINCODE_SERVER_ADDRESS` and `CHAINCODE_ID`, which are described in the `chaincode.env.example` file. Copy this file to `chaincode.env` before continuing. | ||
|
||
**Note:** each organization in a Fabric network will need to follow the instructions below to host their own instance of the FabCar external service. | ||
|
||
## Packaging and installing | ||
|
||
Make sure the value of `CHAINCODE_SERVER_ADDRESS` in `chaincode.env` is correct for the FabCar external service you will be running. | ||
|
||
The peer needs a `connection.json` configuration file so that it can connect to the external FabCar service. | ||
Use the `CHAINCODE_SERVER_ADDRESS` value in `chaincode.env` to create the `connection.json` file with the following command (requires [jq](https://stedolan.github.io/jq/)): | ||
|
||
``` | ||
env $(cat chaincode.env | grep -v "#" | xargs) jq -n '{"address":env.CHAINCODE_SERVER_ADDRESS,"dial_timeout": "10s","tls_required": false}' > connection.json | ||
``` | ||
|
||
Add this file to a `code.tar.gz` archive ready for adding to a FabCar external service package: | ||
|
||
``` | ||
tar cfz code.tar.gz connection.json | ||
``` | ||
|
||
Package the FabCar external service using the supplied `metadata.json` file: | ||
|
||
``` | ||
tar cfz fabcar-pkg.tgz metadata.json code.tar.gz | ||
``` | ||
|
||
Install the `fabcar-pkg.tgz` chaincode as usual, for example: | ||
|
||
``` | ||
peer lifecycle chaincode install ./fabcar-pkg.tgz | ||
``` | ||
|
||
## Running the FabCar external service | ||
|
||
To run the service in a container, build a FabCar docker image: | ||
|
||
``` | ||
docker build -t hyperledger/fabcar-sample . | ||
``` | ||
|
||
Edit the `chaincode.env` file to configure the `CHAINCODE_ID` variable before starting a FabCar container using the following command: | ||
|
||
``` | ||
docker run -it --rm --name fabcar.org1.example.com --hostname fabcar.org1.example.com --env-file chaincode.env --network=net_test hyperledger/fabcar-sample | ||
``` | ||
|
||
## Starting the FabCar external service | ||
|
||
Complete the remaining lifecycle steps to start the FabCar chaincode! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can | ||
# connect to the chaincode server | ||
CHAINCODE_SERVER_ADDRESS=fabcar.org1.example.com:9999 | ||
|
||
# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode | ||
# on install. The `peer lifecycle chaincode queryinstalled` command can be | ||
# used to get the ID after install if required | ||
CHAINCODE_ID=fabcar:... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"strconv" | ||
"os" | ||
|
||
"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 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{ | ||
Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"}, | ||
Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"}, | ||
Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"}, | ||
Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"}, | ||
Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"}, | ||
Car{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"}, | ||
Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"}, | ||
Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"}, | ||
Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"}, | ||
Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"}, | ||
} | ||
|
||
for i, car := range cars { | ||
carAsBytes, _ := json.Marshal(car) | ||
err := ctx.GetStub().PutState("CAR"+strconv.Itoa(i), carAsBytes) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Failed to put to world state. %s", err.Error()) | ||
} | ||
} | ||
|
||
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, _ := json.Marshal(car) | ||
|
||
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. %s", err.Error()) | ||
} | ||
|
||
if carAsBytes == nil { | ||
return nil, fmt.Errorf("%s does not exist", carNumber) | ||
} | ||
|
||
car := new(Car) | ||
_ = json.Unmarshal(carAsBytes, car) | ||
|
||
return car, nil | ||
} | ||
|
||
// QueryAllCars returns all cars found in world state | ||
func (s *SmartContract) QueryAllCars(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) { | ||
startKey := "CAR0" | ||
endKey := "CAR99" | ||
|
||
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
defer resultsIterator.Close() | ||
|
||
results := []QueryResult{} | ||
|
||
for resultsIterator.HasNext() { | ||
queryResponse, err := resultsIterator.Next() | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
car := new(Car) | ||
_ = json.Unmarshal(queryResponse.Value, car) | ||
|
||
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, _ := json.Marshal(car) | ||
|
||
return ctx.GetStub().PutState(carNumber, carAsBytes) | ||
} | ||
|
||
func main() { | ||
// See chaincode.env.example | ||
config := ServerConfig{ | ||
CCID: os.Getenv("CHAINCODE_ID"), | ||
Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"), | ||
} | ||
|
||
chaincode, err := contractapi.NewChaincode(new(SmartContract)) | ||
|
||
if err != nil { | ||
fmt.Printf("Error create fabcar chaincode: %s", err.Error()) | ||
return | ||
} | ||
|
||
server := &shim.ChaincodeServer{ | ||
CCID: config.CCID, | ||
Address: config.Address, | ||
CC: chaincode, | ||
TLSProps: shim.TLSProperties{ | ||
Disabled: true, | ||
}, | ||
} | ||
|
||
if err := server.Start(); err != nil { | ||
fmt.Printf("Error starting fabcar chaincode: %s", err.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/hyperledger/fabric-samples/chaincode/fabcar/external | ||
|
||
go 1.13 | ||
|
||
require github.com/hyperledger/fabric-contract-api-go v1.0.0 |
Oops, something went wrong.