mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 23:45:10 +00:00
* Enable TLS on asset-transfer-basic external chaincode 1. Introduce environment variables to enable or disable TLS at runtime. Also, introduce environment variables which carries the server key, cert and the client machine's root CA cert information. 2. Read the environment variables to decide with appropriate assumptions on default values to work as is today. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com> * Update the chaincode.env with new options 1. Introduce four new environment variables to the default chaincode environment file. 2. Add two new environment files for easy/automation running of the external chaincodes for different organizations. Note that when TLS enabled, the package contents will change. So each organization will have their own package ID generated unlike earlier. Also, there will be two chaincode containers running instead of one for the given example. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com> * Add docker compose file for running chaincodes The file will build and run the chaincode on the fly. Also, it will make use of the available files which is configured by the user as part of execution. This will help in bringup/bringdown of the chaincode containers. In addition, it will help in updating the environment variables on the go. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com> * Introduce crypto directory and ignore the pem contents The directory is used by the docker-compose file for automation. Instructions in the README.md file will suffice the filling up of required files in this directory. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com> * Instructions on TLS enabled external chaincode server 1. Add instructions to generate a chaincode server TLS parameters. 2. Add instructions on different parameters that peer node expects and the chaincode expects. 3. Add instructions on steps to be followed in asset-transfer -basic to work with iTLS enabled exteral chaincode. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com> * Enable chaincode to work without TLS as well 1. Root certificate is an optional parameter supplied for chaincode to verify the peer's connection in response. 2. If TLS is enabled, then specify the key and cert parameters. Do not expect it otherwise. Signed-off-by: S m, Aruna <arun.s.m.cse@gmail.com>
291 lines
7.4 KiB
Go
291 lines
7.4 KiB
Go
/*
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
|
|
"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 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 cars 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 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 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)
|
|
}
|
|
|
|
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 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)
|
|
}
|
|
|
|
// 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()
|
|
|
|
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
|
|
}
|
|
|
|
func main() {
|
|
// See chaincode.env.example
|
|
config := serverConfig{
|
|
CCID: os.Getenv("CHAINCODE_ID"),
|
|
Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"),
|
|
}
|
|
|
|
chaincode, err := contractapi.NewChaincode(&SmartContract{})
|
|
|
|
if err != nil {
|
|
log.Panicf("error create asset-transfer-basic chaincode: %s", err)
|
|
}
|
|
|
|
server := &shim.ChaincodeServer{
|
|
CCID: config.CCID,
|
|
Address: config.Address,
|
|
CC: chaincode,
|
|
TLSProps: getTLSProperties(),
|
|
}
|
|
|
|
if err := server.Start(); err != nil {
|
|
log.Panicf("error starting asset-transfer-basic chaincode: %s", err)
|
|
}
|
|
}
|
|
|
|
func getTLSProperties() shim.TLSProperties {
|
|
// Check if chaincode is TLS enabled
|
|
tlsDisabledStr := getEnvOrDefault("CHAINCODE_TLS_DISABLED", "true")
|
|
key := getEnvOrDefault("CHAINCODE_TLS_KEY", "")
|
|
cert := getEnvOrDefault("CHAINCODE_TLS_CERT", "")
|
|
clientCACert := getEnvOrDefault("CHAINCODE_CLIENT_CA_CERT", "")
|
|
|
|
// convert tlsDisabledStr to boolean
|
|
tlsDisabled := getBoolOrDefault(tlsDisabledStr, false)
|
|
var keyBytes, certBytes, clientCACertBytes []byte
|
|
var err error
|
|
|
|
if !tlsDisabled {
|
|
keyBytes, err = ioutil.ReadFile(key)
|
|
if err != nil {
|
|
log.Panicf("error while reading the crypto file: %s", err)
|
|
}
|
|
certBytes, err = ioutil.ReadFile(cert)
|
|
if err != nil {
|
|
log.Panicf("error while reading the crypto file: %s", err)
|
|
}
|
|
}
|
|
// Did not request for the peer cert verification
|
|
if clientCACert != "" {
|
|
clientCACertBytes, err = ioutil.ReadFile(clientCACert)
|
|
if err != nil {
|
|
log.Panicf("error while reading the crypto file: %s", err)
|
|
}
|
|
}
|
|
|
|
return shim.TLSProperties{
|
|
Disabled: tlsDisabled,
|
|
Key: keyBytes,
|
|
Cert: certBytes,
|
|
ClientCACerts: clientCACertBytes,
|
|
}
|
|
}
|
|
|
|
func getEnvOrDefault(env, defaultVal string) string {
|
|
value, ok := os.LookupEnv(env)
|
|
if !ok {
|
|
value = defaultVal
|
|
}
|
|
return value
|
|
}
|
|
|
|
// Note that the method returns default value if the string
|
|
// cannot be parsed!
|
|
func getBoolOrDefault(value string, defaultVal bool) bool {
|
|
parsed, err := strconv.ParseBool(value)
|
|
if err!= nil {
|
|
return defaultVal
|
|
}
|
|
return parsed
|
|
}
|