├── metadata.json ├── connection.json ├── go.mod ├── Dockerfile ├── README.md ├── chaincode1.env ├── chaincode2.env ├── chaincode.env ├── .github └── workflows │ └── publish.yaml ├── assetTransfer.go ├── go.sum └── README_OLD.md /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "external", 3 | "label": "basic_1.0" 4 | } 5 | -------------------------------------------------------------------------------- /connection.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "asset-transfer-basic.org1.example.com:9999", 3 | "dial_timeout": "10s", 4 | "tls_required": false 5 | } 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 7 | github.com/hyperledger/fabric-contract-api-go v1.1.0 8 | ) 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | ARG GO_VER=1.14.4 6 | ARG ALPINE_VER=3.12 7 | 8 | FROM golang:${GO_VER}-alpine${ALPINE_VER} 9 | 10 | WORKDIR /go/src/github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external 11 | COPY . . 12 | 13 | RUN go get -d -v ./... 14 | RUN go install -v ./... 15 | 16 | EXPOSE 9999 17 | CMD ["chaincode-external"] 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fabric-external-asset-transfer-basic 2 | 3 | This project serves as a guide for running the [asset-transfer-basic](https://github.com/hyperledger/fabric-samples/tree/main/asset-transfer-basic/chaincode-external) 4 | [Chaincode as a Service](https://hyperledger-fabric.readthedocs.io/en/latest/cc_service.html) and using [Chaincode External Builders](https://hyperledger-fabric.readthedocs.io/en/latest/cc_launcher.html) 5 | configured for Kubernetes. 6 | 7 | A sample integration is available with the [Kubernetes Test Network](https://github.com/jkneubuh/fabric-samples/tree/main/test-network-k8s) 8 | 9 | 10 | ## Quickstart 11 | 12 | ```shell 13 | docker run \ 14 | --rm \ 15 | -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 \ 16 | -e CHAINCODE_ID=basic_1.0:e47a28f7406718bb0c6cefb00a6a6167099bf2d426e802d417f54c32d1a1ea1b \ 17 | -p 9999:9999 \ 18 | ghcr.io/hyperledgendary/fabric-external-asset-transfer-basic 19 | ``` 20 | 21 | 22 | ## Building the Image 23 | 24 | 25 | ## Publishing the Image 26 | 27 | 28 | ## Updating the Chaincode Deployment -------------------------------------------------------------------------------- /chaincode1.env: -------------------------------------------------------------------------------- 1 | # CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can 2 | # connect to the chaincode server 3 | CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999 4 | 5 | # CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode 6 | # on install. The `peer lifecycle chaincode queryinstalled` command can be 7 | # used to get the ID after install if required 8 | CHAINCODE_ID=basic_1.0:6726c6b6d8ff66fcf5710b72c6ce512d24f118c51c3de510b3d43e51fa592a7d 9 | 10 | # Optional parameters that will be used for TLS connection between peer node 11 | # and the chaincode. 12 | # TLS is disabled by default, uncomment the following line to enable TLS connection 13 | CHAINCODE_TLS_DISABLED=false 14 | 15 | # Following variables will be ignored if TLS is not enabled. 16 | # They need to be in PEM format 17 | CHAINCODE_TLS_KEY=/crypto/key1.pem 18 | CHAINCODE_TLS_CERT=/crypto/cert1.pem 19 | 20 | # The following variable will be used by the chaincode server to verify the 21 | # connection from the peer node. 22 | # Note that when this is set a single chaincode server cannot be shared 23 | # across organizations unless their root CA is same. 24 | CHAINCODE_CLIENT_CA_CERT=/crypto/rootcert1.pem 25 | -------------------------------------------------------------------------------- /chaincode2.env: -------------------------------------------------------------------------------- 1 | # CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can 2 | # connect to the chaincode server 3 | CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org2.example.com:9999 4 | 5 | # CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode 6 | # on install. The `peer lifecycle chaincode queryinstalled` command can be 7 | # used to get the ID after install if required 8 | CHAINCODE_ID=basic_1.0:e8f9052385e3763ecf5635591155da05d8efbb6905ccbfc1c7229eb6bd28df1b 9 | 10 | # Optional parameters that will be used for TLS connection between peer node 11 | # and the chaincode. 12 | # TLS is disabled by default, uncomment the following line to enable TLS connection 13 | CHAINCODE_TLS_DISABLED=false 14 | 15 | # Following variables will be ignored if TLS is not enabled. 16 | # They need to be in PEM format 17 | CHAINCODE_TLS_KEY=/crypto/key2.pem 18 | CHAINCODE_TLS_CERT=/crypto/cert2.pem 19 | 20 | # The following variable will be used by the chaincode server to verify the 21 | # connection from the peer node. 22 | # Note that when this is set a single chaincode server cannot be shared 23 | # across organizations unless their root CA is same. 24 | CHAINCODE_CLIENT_CA_CERT=/crypto/rootcert2.pem 25 | -------------------------------------------------------------------------------- /chaincode.env: -------------------------------------------------------------------------------- 1 | # CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can 2 | # connect to the chaincode server 3 | CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999 4 | 5 | # CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode 6 | # on install. The `peer lifecycle chaincode queryinstalled` command can be 7 | # used to get the ID after install if required 8 | CHAINCODE_ID=basic_1.0:0262396ccaffaa2174bc09f750f742319c4f14d60b16334d2c8921b6842c090c 9 | 10 | # Optional parameters that will be used for TLS connection between peer node 11 | # and the chaincode. 12 | # TLS is disabled by default, uncomment the following line to enable TLS connection 13 | # CHAINCODE_TLS_DISABLED=false 14 | 15 | # Following variables will be ignored if TLS is not enabled. 16 | # They need to be in PEM format 17 | # CHAINCODE_TLS_KEY=/path/to/private/key/file 18 | # CHAINCODE_TLS_CERT=/path/to/public/cert/file 19 | 20 | # The following variable will be used by the chaincode server to verify the 21 | # connection from the peer node. 22 | # Note that when this is set a single chaincode server cannot be shared 23 | # across organizations unless their root CA is same. 24 | # CHAINCODE_CLIENT_CA_CERT=/path/to/peer/organization/root/ca/cert/file 25 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: fabric-ccaas-asset-transfer-basic 2 | 3 | on: 4 | push: 5 | # Publish `main` as Docker `latest` image. 6 | branches: 7 | - main 8 | 9 | # Publish `v1.2.3` tags as releases. 10 | tags: 11 | - v* 12 | 13 | # Run tests for any PRs. 14 | pull_request: 15 | 16 | env: 17 | IMAGE_NAME: fabric-ccaas-asset-transfer-basic 18 | 19 | jobs: 20 | # Push image to GitHub Packages. 21 | # See also https://docs.docker.com/docker-hub/builds/ 22 | push: 23 | runs-on: ubuntu-latest 24 | permissions: 25 | packages: write 26 | contents: read 27 | 28 | steps: 29 | - uses: actions/checkout@v2 30 | 31 | - name: Build image 32 | run: docker build --tag $IMAGE_NAME --label "runnumber=${GITHUB_RUN_ID}" . 33 | 34 | - name: Log in to registry 35 | # This is where you will update the PAT to GITHUB_TOKEN 36 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 37 | 38 | - name: Push image 39 | run: | 40 | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME 41 | 42 | # Change all uppercase to lowercase 43 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 44 | # Strip git ref prefix from version 45 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 46 | # Strip "v" prefix from tag name 47 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') 48 | # Use Docker `latest` tag convention 49 | [ "$VERSION" == "main" ] && VERSION=latest 50 | echo IMAGE_ID=$IMAGE_ID 51 | echo VERSION=$VERSION 52 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 53 | docker push $IMAGE_ID:$VERSION -------------------------------------------------------------------------------- /assetTransfer.go: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "strconv" 14 | 15 | "github.com/hyperledger/fabric-chaincode-go/shim" 16 | "github.com/hyperledger/fabric-contract-api-go/contractapi" 17 | ) 18 | 19 | type serverConfig struct { 20 | CCID string 21 | Address string 22 | } 23 | 24 | // SmartContract provides functions for managing an asset 25 | type SmartContract struct { 26 | contractapi.Contract 27 | } 28 | 29 | // Asset describes basic details of what makes up a simple asset 30 | type Asset struct { 31 | ID string `json:"ID"` 32 | Color string `json:"color"` 33 | Size int `json:"size"` 34 | Owner string `json:"owner"` 35 | AppraisedValue int `json:"appraisedValue"` 36 | } 37 | 38 | // QueryResult structure used for handling result of query 39 | type QueryResult struct { 40 | Key string `json:"Key"` 41 | Record *Asset 42 | } 43 | 44 | // InitLedger adds a base set of cars to the ledger 45 | func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { 46 | assets := []Asset{ 47 | {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, 48 | {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, 49 | {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, 50 | {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, 51 | {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, 52 | {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, 53 | } 54 | 55 | for _, asset := range assets { 56 | assetJSON, err := json.Marshal(asset) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | err = ctx.GetStub().PutState(asset.ID, assetJSON) 62 | if err != nil { 63 | return fmt.Errorf("failed to put to world state: %v", err) 64 | } 65 | } 66 | 67 | return nil 68 | } 69 | 70 | // CreateAsset issues a new asset to the world state with given details. 71 | func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, color string, size int, owner string, appraisedValue int) error { 72 | exists, err := s.AssetExists(ctx, id) 73 | if err != nil { 74 | return err 75 | } 76 | if exists { 77 | return fmt.Errorf("the asset %s already exists", id) 78 | } 79 | asset := Asset{ 80 | ID: id, 81 | Color: color, 82 | Size: size, 83 | Owner: owner, 84 | AppraisedValue: appraisedValue, 85 | } 86 | 87 | assetJSON, err := json.Marshal(asset) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | return ctx.GetStub().PutState(id, assetJSON) 93 | } 94 | 95 | // ReadAsset returns the asset stored in the world state with given id. 96 | func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) { 97 | assetJSON, err := ctx.GetStub().GetState(id) 98 | if err != nil { 99 | return nil, fmt.Errorf("failed to read from world state. %s", err.Error()) 100 | } 101 | if assetJSON == nil { 102 | return nil, fmt.Errorf("the asset %s does not exist", id) 103 | } 104 | 105 | var asset Asset 106 | err = json.Unmarshal(assetJSON, &asset) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | return &asset, nil 112 | } 113 | 114 | // UpdateAsset updates an existing asset in the world state with provided parameters. 115 | func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id, color string, size int, owner string, appraisedValue int) error { 116 | exists, err := s.AssetExists(ctx, id) 117 | if err != nil { 118 | return err 119 | } 120 | if !exists { 121 | return fmt.Errorf("the asset %s does not exist", id) 122 | } 123 | 124 | // overwritting original asset with new asset 125 | asset := Asset{ 126 | ID: id, 127 | Color: color, 128 | Size: size, 129 | Owner: owner, 130 | AppraisedValue: appraisedValue, 131 | } 132 | 133 | assetJSON, err := json.Marshal(asset) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | return ctx.GetStub().PutState(id, assetJSON) 139 | } 140 | 141 | // DeleteAsset deletes an given asset from the world state. 142 | func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error { 143 | exists, err := s.AssetExists(ctx, id) 144 | if err != nil { 145 | return err 146 | } 147 | if !exists { 148 | return fmt.Errorf("the asset %s does not exist", id) 149 | } 150 | 151 | return ctx.GetStub().DelState(id) 152 | } 153 | 154 | // AssetExists returns true when asset with given ID exists in world state 155 | func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { 156 | assetJSON, err := ctx.GetStub().GetState(id) 157 | if err != nil { 158 | return false, fmt.Errorf("failed to read from world state. %s", err.Error()) 159 | } 160 | 161 | return assetJSON != nil, nil 162 | } 163 | 164 | // TransferAsset updates the owner field of asset with given id in world state. 165 | func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { 166 | asset, err := s.ReadAsset(ctx, id) 167 | if err != nil { 168 | return err 169 | } 170 | 171 | asset.Owner = newOwner 172 | assetJSON, err := json.Marshal(asset) 173 | if err != nil { 174 | return err 175 | } 176 | 177 | return ctx.GetStub().PutState(id, assetJSON) 178 | } 179 | 180 | // GetAllAssets returns all assets found in world state 181 | func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) { 182 | // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. 183 | resultsIterator, err := ctx.GetStub().GetStateByRange("", "") 184 | 185 | if err != nil { 186 | return nil, err 187 | } 188 | defer resultsIterator.Close() 189 | 190 | var results []QueryResult 191 | 192 | for resultsIterator.HasNext() { 193 | queryResponse, err := resultsIterator.Next() 194 | 195 | if err != nil { 196 | return nil, err 197 | } 198 | 199 | var asset Asset 200 | err = json.Unmarshal(queryResponse.Value, &asset) 201 | if err != nil { 202 | return nil, err 203 | } 204 | 205 | queryResult := QueryResult{Key: queryResponse.Key, Record: &asset} 206 | results = append(results, queryResult) 207 | } 208 | 209 | return results, nil 210 | } 211 | 212 | func main() { 213 | // See chaincode.env.example 214 | config := serverConfig{ 215 | CCID: os.Getenv("CHAINCODE_ID"), 216 | Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"), 217 | } 218 | 219 | chaincode, err := contractapi.NewChaincode(&SmartContract{}) 220 | 221 | if err != nil { 222 | log.Panicf("error create asset-transfer-basic chaincode: %s", err) 223 | } 224 | 225 | server := &shim.ChaincodeServer{ 226 | CCID: config.CCID, 227 | Address: config.Address, 228 | CC: chaincode, 229 | TLSProps: getTLSProperties(), 230 | } 231 | 232 | if err := server.Start(); err != nil { 233 | log.Panicf("error starting asset-transfer-basic chaincode: %s", err) 234 | } 235 | } 236 | 237 | func getTLSProperties() shim.TLSProperties { 238 | // Check if chaincode is TLS enabled 239 | tlsDisabledStr := getEnvOrDefault("CHAINCODE_TLS_DISABLED", "true") 240 | key := getEnvOrDefault("CHAINCODE_TLS_KEY", "") 241 | cert := getEnvOrDefault("CHAINCODE_TLS_CERT", "") 242 | clientCACert := getEnvOrDefault("CHAINCODE_CLIENT_CA_CERT", "") 243 | 244 | // convert tlsDisabledStr to boolean 245 | tlsDisabled := getBoolOrDefault(tlsDisabledStr, false) 246 | var keyBytes, certBytes, clientCACertBytes []byte 247 | var err error 248 | 249 | if !tlsDisabled { 250 | keyBytes, err = ioutil.ReadFile(key) 251 | if err != nil { 252 | log.Panicf("error while reading the crypto file: %s", err) 253 | } 254 | certBytes, err = ioutil.ReadFile(cert) 255 | if err != nil { 256 | log.Panicf("error while reading the crypto file: %s", err) 257 | } 258 | } 259 | // Did not request for the peer cert verification 260 | if clientCACert != "" { 261 | clientCACertBytes, err = ioutil.ReadFile(clientCACert) 262 | if err != nil { 263 | log.Panicf("error while reading the crypto file: %s", err) 264 | } 265 | } 266 | 267 | return shim.TLSProperties{ 268 | Disabled: tlsDisabled, 269 | Key: keyBytes, 270 | Cert: certBytes, 271 | ClientCACerts: clientCACertBytes, 272 | } 273 | } 274 | 275 | func getEnvOrDefault(env, defaultVal string) string { 276 | value, ok := os.LookupEnv(env) 277 | if !ok { 278 | value = defaultVal 279 | } 280 | return value 281 | } 282 | 283 | // Note that the method returns default value if the string 284 | // cannot be parsed! 285 | func getBoolOrDefault(value string, defaultVal bool) bool { 286 | parsed, err := strconv.ParseBool(value) 287 | if err!= nil { 288 | return defaultVal 289 | } 290 | return parsed 291 | } 292 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0= 4 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 5 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 6 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 7 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 8 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 9 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 10 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 11 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 12 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 13 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 14 | github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A= 15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 19 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 20 | github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= 21 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 22 | github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= 23 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 24 | github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= 25 | github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 26 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 27 | github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= 28 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 29 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 30 | github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= 31 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 32 | github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= 33 | github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= 34 | github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= 35 | github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= 36 | github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= 37 | github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= 38 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 39 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 40 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 41 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 42 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 43 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 44 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 45 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 46 | github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o= 47 | github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc= 48 | github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4= 49 | github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E= 50 | github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= 51 | github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E= 52 | github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= 53 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 54 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 55 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 56 | github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 57 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 58 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 59 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 60 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 61 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 62 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 63 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 64 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 65 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 66 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 67 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 68 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= 69 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 70 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 71 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 72 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 73 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 74 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 75 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 76 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= 77 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 78 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 79 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 80 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 81 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 82 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 83 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 84 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 85 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 86 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 87 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 88 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 89 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 90 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 91 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 92 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 93 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 94 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 95 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 96 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 97 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 98 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 99 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 100 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 101 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 102 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 103 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 104 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 105 | golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 106 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 107 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 108 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 109 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 110 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 111 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= 112 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 113 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 114 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 115 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 116 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 117 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 118 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 119 | golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 120 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 121 | golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= 122 | golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 123 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 124 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 125 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 126 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 127 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 128 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 129 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 130 | golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 131 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 132 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 133 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= 134 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 135 | google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= 136 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 137 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 138 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 139 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 140 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 141 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 142 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 143 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 144 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 145 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 146 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 147 | -------------------------------------------------------------------------------- /README_OLD.md: -------------------------------------------------------------------------------- 1 | # Asset-Transfer-Basic as an external service 2 | 3 | This sample provides an introduction to how to use external builder and launcher scripts to run chaincode as an external service to your peer. For more information, see the [Chaincode as an external service](https://hyperledger-fabric.readthedocs.io/en/latest/cc_service.html) topic in the Fabric documentation. 4 | 5 | **Note:** each organization in a real network would need to setup and host their own instance of the external service. In this tutorial, we use the same instance for both organizations for simplicity. 6 | 7 | ## Setting up the external builder and launcher 8 | 9 | External Builders and Launchers is an advanced feature that typically requires custom packaging of the peer image so that it contains all the tools your builder and launcher require. This sample uses very simple (and crude) shell scripts that can be run directly within the default Fabric peer images. 10 | 11 | Open the `config/core.yaml` file at the top of the `fabric-samples` directory. If you do not have this file, follow the instructions to [Install the Samples, Binaries and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) to download the Fabric binaries and configuration files alongside the Fabric samples. 12 | 13 | Modify the `externalBuilders` field in the `core.yaml` file to resemble the configuration below: 14 | ``` 15 | externalBuilders: 16 | - path: /opt/gopath/src/github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external/sampleBuilder 17 | name: external-sample-builder 18 | ``` 19 | This update sets the name of the external builder as `external-sample-builder`, and the path of the builder to the scripts provided in this sample. Note that this is the path within the peer container, not your local machine. 20 | 21 | To set the path within the peer container, you will need to modify the docker compose file to mount a couple of additional volumes. Open the file `test-network/docker/docker-compose-test-net.yaml`, and add to the `volumes` section of both `peer0.org1.example.com` and `peer0.org2.example.com` the following two lines: 22 | 23 | ``` 24 | - ../..:/opt/gopath/src/github.com/hyperledger/fabric-samples 25 | - ../../config/core.yaml:/etc/hyperledger/fabric/core.yaml 26 | ``` 27 | 28 | This update will mount the `core.yaml` that you modified into the peer container and override the configuration file within the peer image. The update also mounts the fabric-sample builder so that it can be found at the location that you specified in `core.yaml`. You also have the option of commenting out the line `- /var/run/docker.sock:/host/var/run/docker.sock`, since we no longer need to access the docker daemon from inside the peer container to launch the chaincode. 29 | 30 | ## Packaging and installing Chaincode 31 | 32 | The Asset-Transfer-Basic external chaincode requires two environment variables to run, `CHAINCODE_SERVER_ADDRESS` and `CHAINCODE_ID`, which are described and set in the `chaincode.env` file. 33 | 34 | You need to provide a `connection.json` configuration file to your peer in order to connect to the external Asset-Transfer-Basic service. The address specified in the `connection.json` must correspond to the `CHAINCODE_SERVER_ADDRESS` value in `chaincode.env`, which is `asset-transfer-basic.org1.example.com:9999` in our example. 35 | 36 | Because we will run our chaincode as an external service, the chaincode itself does not need to be included in the chaincode 37 | package that gets installed to each peer. Only the configuration and metadata information needs to be included 38 | in the package. Since the packaging is trivial, we can manually create the chaincode package. 39 | 40 | Open a new terminal and navigate to the `fabric-samples/asset-transfer-basic/chaincode-external` directory. 41 | ``` 42 | cd fabric-samples/asset-transfer-basic/chaincode-external 43 | ``` 44 | 45 | First, create a `code.tar.gz` archive containing the `connection.json` file: 46 | ``` 47 | tar cfz code.tar.gz connection.json 48 | ``` 49 | 50 | Then, create the chaincode package, including the `code.tar.gz` file and the supplied `metadata.json` file: 51 | ``` 52 | tar cfz asset-transfer-basic-external.tgz metadata.json code.tar.gz 53 | ``` 54 | 55 | You are now ready to deploy the external chaincode sample. 56 | 57 | ## Starting the test network 58 | 59 | We will use the Fabric test network to run the external chaincode. Open a new terminal and navigate to the `fabric-samples/test-network` directory. 60 | ``` 61 | cd fabric-samples/test-network 62 | ``` 63 | 64 | Run the following command to deploy the test network and create a new channel: 65 | ``` 66 | ./network.sh up createChannel -c mychannel -ca 67 | ``` 68 | 69 | We are now ready to deploy the external chaincode. 70 | 71 | ## Installing the external chaincode 72 | 73 | We can't use the test network script to install an external chaincode so we will have to do a bit more work. However, we can still leverage part of the test-network scripts to make this easier. 74 | 75 | From the `test-network` directory, set the following environment variables to use the Fabric binaries: 76 | ``` 77 | export PATH=${PWD}/../bin:$PATH 78 | export FABRIC_CFG_PATH=$PWD/../config/ 79 | ``` 80 | 81 | Run the following command to import functions from the `envVar.sh` script into your terminal. These functions allow you to act as either test network organization. 82 | ``` 83 | . ./scripts/envVar.sh 84 | ``` 85 | 86 | Run the following commands to install the `asset-transfer-basic-external.tar.gz` chaincode on org1. The `setGlobals` function simply sets the environment variables that allow you to act as org1 or org2. 87 | ``` 88 | setGlobals 1 89 | peer lifecycle chaincode install ../asset-transfer-basic/chaincode-external/asset-transfer-basic-external.tgz 90 | ``` 91 | 92 | Install the chaincode package on the org2 peer: 93 | ``` 94 | setGlobals 2 95 | peer lifecycle chaincode install ../asset-transfer-basic/chaincode-external/asset-transfer-basic-external.tgz 96 | ``` 97 | 98 | Run the following command to query the package ID of the chaincode that you just installed: 99 | ``` 100 | setGlobals 1 101 | peer lifecycle chaincode queryinstalled --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 102 | ``` 103 | 104 | The command will return output similar to the following: 105 | ``` 106 | Installed chaincodes on peer: 107 | Package ID: basic_1.0:ecfc83f251b7c2d9ef376bc3fc20fc6b9744c0fc0a8923092af6542af94790c3, Label: basic_1.0 108 | ``` 109 | 110 | Save the package ID that was returned by the command as an environment variable. The ID will not be the same for all users, so you need to set the variable using the ID from your command window: 111 | ``` 112 | export CHAINCODE_ID=basic_1.0:ecfc83f251b7c2d9ef376bc3fc20fc6b9744c0fc0a8923092af6542af94790c3 113 | ``` 114 | 115 | 116 | ## Running the Asset-Transfer-Basic external service 117 | 118 | We are going to run the smart contract as an external service by first building and then starting a docker container. Open a new terminal and navigate back to the `chaincode-external` directory: 119 | ``` 120 | cd fabric-samples/asset-transfer-basic/chaincode-external 121 | ``` 122 | 123 | In this directory, open the `chaincode.env` file to set the `CHAINCODE_ID` variable to the same package ID that was returned by the `peer lifecycle chaincode queryinstalled` command. The value should be the same as the environment variable that you set in the previous terminal. 124 | 125 | After you edit the `chaincode.env` file, you can use the `Dockerfile` to build an image of the external Asset-Transfer-Basic chaincode: 126 | ``` 127 | docker build -t hyperledger/asset-transfer-basic . 128 | ``` 129 | 130 | You can then run the image to start the Asset-Transfer-Basic service: 131 | ``` 132 | docker run -it --rm --name asset-transfer-basic.org1.example.com --hostname asset-transfer-basic.org1.example.com --env-file chaincode.env --network=fabric_test hyperledger/asset-transfer-basic 133 | ``` 134 | 135 | This will start and run the external chaincode service within the container. 136 | 137 | ## Deploy the Asset-Transfer-Basic external chaincode definition to the channel 138 | 139 | Navigate back to the `test-network` directory to finish deploying the chaincode definition of the external smart contract to the channel. Make sure that your environment variables are still set. 140 | 141 | ``` 142 | setGlobals 2 143 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --channelID mychannel --name basic --version 1.0 --package-id $CHAINCODE_ID --sequence 1 144 | 145 | setGlobals 1 146 | peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --channelID mychannel --name basic --version 1.0 --package-id $CHAINCODE_ID --sequence 1 147 | 148 | peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --channelID mychannel --name basic --peerAddresses localhost:7051 --tlsRootCertFiles "$PWD/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1 149 | ``` 150 | 151 | The commands above approve the chaincode definition for the external chaincode and commits the definition to the channel. The resulting output should be similar to the following: 152 | 153 | ``` 154 | 2020-08-05 15:41:44.982 PDT [chaincodeCmd] ClientWait -> INFO 001 txid [6bdbe040b99a45cc90a23ec21f02ea5da7be8b70590eb04ff3323ef77fdedfc7] committed with status (VALID) at localhost:7051 155 | 2020-08-05 15:41:44.983 PDT [chaincodeCmd] ClientWait -> INFO 002 txid [6bdbe040b99a45cc90a23ec21f02ea5da7be8b70590eb04ff3323ef77fdedfc7] committed with status (VALID) at localhost:9051 156 | ``` 157 | 158 | Now that we have started the chaincode service and deployed it to the channel, we can submit transactions as we would with a normal chaincode. 159 | 160 | ## Using the Asset-Transfer-Basic external chaincode 161 | 162 | Open yet another terminal and navigate to the `fabric-samples/asset-transfer-basic/application-javascript` directory: 163 | ``` 164 | cd fabric-samples/asset-transfer-basic/application-javascript 165 | ``` 166 | 167 | Run the following commands to use the node application in this directory to test the external smart contract: 168 | ``` 169 | rm -rf wallet # in case you ran this before 170 | npm install 171 | node app.js 172 | ``` 173 | 174 | If all goes well, the program should run exactly the same as described in the "Writing Your First Application" tutorial. 175 | 176 | ## Enabling TLS for chaincode and peer communication 177 | 178 | **Note:** This section uses an example of self-signed certificate. You may use your organization hosted CA to issue the certificate and generate a key for production deployment. 179 | 180 | In the sample so far, you connected both peers in `test-network` to the single instance of chaincode server. However, if you would like to enable TLS between the peer nodes and the chaincode server, each peer node needs to have its own CA certificate. Enabling TLS is made possible at runtime in the chaincode. 181 | 182 | - As a first step generate a keypair that can be used. Run these commands from the `fabric-samples/asset-transfer-basic/chaincode-external` directory. 183 | 184 | *Find instructions to install `openssl` in [openssl.org](https://www.openssl.org/)* 185 | 186 | For `org1.example.com` 187 | 188 | ``` 189 | openssl req -nodes -x509 -newkey rsa:4096 -keyout crypto/key1.pem -out crypto/cert1.pem -subj "/C=IN/ST=KA/L=Bangalore/O=example Inc/OU=Developer/CN=asset-transfer-basic.org1.example.com/emailAddress=dev@asset-transfer-basic.org1.example.com" 190 | ``` 191 | 192 | For `org2.example.com` 193 | 194 | ``` 195 | openssl req -nodes -x509 -newkey rsa:4096 -keyout crypto/key2.pem -out crypto/cert2.pem -subj "/C=IN/ST=KA/L=Bangalore/O=example Inc/OU=Developer/CN=asset-transfer-basic.org2.example.com/emailAddress=dev@asset-transfer-basic.org2.example.com" 196 | ``` 197 | 198 | - Copy the CA file contents for both `org1.example.com` & `org2.example.com` 199 | 200 | ``` 201 | cp ../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem crypto/rootcert1.pem 202 | cp ../../test-network/organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem crypto/rootcert2.pem 203 | ``` 204 | 205 | - Generate a client key and cert for auth purpose. You need a key and cert generated from the CA of each organization. Peer nodes act as clients to chaincode server. 206 | 207 | - Change the `connection.json` with the below contents. The `root_cert` parameter is the root CA certificate which the chaincode server is run with. You may run the below commands to get the certificate file contents as strings and copy them when needed. 208 | 209 | ``` 210 | awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' crypto/cert1.pem 211 | awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' crypto/cert2.pem 212 | ``` 213 | 214 | Similarly, replace the `client_key` and the `client_cert` contents with the values from the previous step. 215 | 216 | ``` 217 | { 218 | "address": "asset-transfer-basic.org1.example.com:9999", 219 | "dial_timeout": "10s", 220 | "tls_required": true, 221 | "client_auth_required": true, 222 | "client_key": "-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----", 223 | "client_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----", 224 | "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----" 225 | } 226 | ``` 227 | 228 | - Follow the instructions in [Package](#packaging-and-installing-chaincode) and [Install](#installing-the-external-chaincode) steps for each organization. Remember that the chaincode server's address for the second organization is `asset-transfer-basic.org2.example.com:9999`. 229 | 230 | - Copy the appropriate `CHAINCODE_ID` to both [chaincode1.env](./chaincode1.env) and [chaincode2.env](./chaincode2.env) files. Bring up the chaincode containers using the docker-compose command below 231 | 232 | ``` 233 | docker-compose up -f docker-compose-chaincode.yaml up --build -d 234 | ``` 235 | 236 | - Follow the instructions in [Finish Deployment](#finish-deploying-the-asset-transfer-basic-external-chaincode-) for each organization seperately. 237 | --------------------------------------------------------------------------------