-
Notifications
You must be signed in to change notification settings - Fork 516
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FAB-1163] - prevent fabric network bounce
- Also includes changes for FAB-1134 - Enable response checking as an explicit step - Do not tear down fabric network between steps - Split features to drive different chaincodes - remove unnecessary 'deploy' feature that is implicitly covered in other features Change-Id: Id466909ec1eac87330cb7aad47c8f336398daee6 Signed-off-by: nkl199@yahoo.co.uk <nkl199@yahoo.co.uk>
- Loading branch information
Showing
24 changed files
with
815 additions
and
207 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,204 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
/* | ||
* The sample smart contract for documentation topic: | ||
* Writing Your First Blockchain Application | ||
*/ | ||
|
||
package main | ||
|
||
/* Imports | ||
* 4 utility libraries for formatting, handling bytes, reading and writing JSON, and string manipulation | ||
* 2 specific Hyperledger Fabric specific libraries for Smart Contracts | ||
*/ | ||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/hyperledger/fabric/core/chaincode/shim" | ||
sc "github.com/hyperledger/fabric/protos/peer" | ||
) | ||
|
||
// Define the Smart Contract structure | ||
type SmartContract struct { | ||
} | ||
|
||
// Define the car structure, with 4 properties. Structure tags are used by encoding/json library | ||
type Car struct { | ||
Make string `json:"make"` | ||
Model string `json:"model"` | ||
Colour string `json:"colour"` | ||
Owner string `json:"owner"` | ||
} | ||
|
||
/* | ||
* The Init method is called when the Smart Contract "fabcar" is instantiated by the blockchain network | ||
* Best practice is to have any Ledger initialization in separate function -- see initLedger() | ||
*/ | ||
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response { | ||
return shim.Success(nil) | ||
} | ||
|
||
/* | ||
* The Invoke method is called as a result of an application request to run the Smart Contract "fabcar" | ||
* The calling application program has also specified the particular smart contract function to be called, with arguments | ||
*/ | ||
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response { | ||
|
||
// Retrieve the requested Smart Contract function and arguments | ||
function, args := APIstub.GetFunctionAndParameters() | ||
// Route to the appropriate handler function to interact with the ledger appropriately | ||
if function == "queryCar" { | ||
return s.queryCar(APIstub, args) | ||
} else if function == "initLedger" { | ||
return s.initLedger(APIstub) | ||
} else if function == "createCar" { | ||
return s.createCar(APIstub, args) | ||
} else if function == "queryAllCars" { | ||
return s.queryAllCars(APIstub) | ||
} else if function == "changeCarOwner" { | ||
return s.changeCarOwner(APIstub, args) | ||
} | ||
|
||
return shim.Error("Invalid Smart Contract function name.") | ||
} | ||
|
||
func (s *SmartContract) queryCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { | ||
|
||
if len(args) != 1 { | ||
return shim.Error("Incorrect number of arguments. Expecting 1") | ||
} | ||
|
||
carAsBytes, _ := APIstub.GetState(args[0]) | ||
return shim.Success(carAsBytes) | ||
} | ||
|
||
func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response { | ||
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"}, | ||
} | ||
|
||
i := 0 | ||
for i < len(cars) { | ||
fmt.Println("i is ", i) | ||
carAsBytes, _ := json.Marshal(cars[i]) | ||
APIstub.PutState("CAR"+strconv.Itoa(i), carAsBytes) | ||
fmt.Println("Added", cars[i]) | ||
i = i + 1 | ||
} | ||
|
||
return shim.Success(nil) | ||
} | ||
|
||
func (s *SmartContract) createCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { | ||
|
||
if len(args) != 5 { | ||
return shim.Error("Incorrect number of arguments. Expecting 5") | ||
} | ||
|
||
var car = Car{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4]} | ||
|
||
carAsBytes, _ := json.Marshal(car) | ||
APIstub.PutState(args[0], carAsBytes) | ||
|
||
return shim.Success(nil) | ||
} | ||
|
||
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response { | ||
|
||
startKey := "CAR0" | ||
endKey := "CAR999" | ||
|
||
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey) | ||
if err != nil { | ||
return shim.Error(err.Error()) | ||
} | ||
defer resultsIterator.Close() | ||
|
||
// buffer is a JSON array containing QueryResults | ||
var buffer bytes.Buffer | ||
buffer.WriteString("[") | ||
|
||
bArrayMemberAlreadyWritten := false | ||
for resultsIterator.HasNext() { | ||
queryResponse, err := resultsIterator.Next() | ||
if err != nil { | ||
return shim.Error(err.Error()) | ||
} | ||
// Add a comma before array members, suppress it for the first array member | ||
if bArrayMemberAlreadyWritten == true { | ||
buffer.WriteString(",") | ||
} | ||
buffer.WriteString("{\"Key\":") | ||
buffer.WriteString("\"") | ||
buffer.WriteString(queryResponse.Key) | ||
buffer.WriteString("\"") | ||
|
||
buffer.WriteString(", \"Record\":") | ||
// Record is a JSON object, so we write as-is | ||
buffer.WriteString(string(queryResponse.Value)) | ||
buffer.WriteString("}") | ||
bArrayMemberAlreadyWritten = true | ||
} | ||
buffer.WriteString("]") | ||
|
||
fmt.Printf("- queryAllCars:\n%s\n", buffer.String()) | ||
|
||
return shim.Success(buffer.Bytes()) | ||
} | ||
|
||
func (s *SmartContract) changeCarOwner(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { | ||
|
||
if len(args) != 2 { | ||
return shim.Error("Incorrect number of arguments. Expecting 2") | ||
} | ||
|
||
carAsBytes, _ := APIstub.GetState(args[0]) | ||
car := Car{} | ||
|
||
json.Unmarshal(carAsBytes, &car) | ||
car.Owner = args[1] | ||
|
||
carAsBytes, _ = json.Marshal(car) | ||
APIstub.PutState(args[0], carAsBytes) | ||
|
||
return shim.Success(nil) | ||
} | ||
|
||
// The main function is only relevant in unit test mode. Only included here for completeness. | ||
func main() { | ||
|
||
// Create a new Smart Contract | ||
err := shim.Start(new(SmartContract)) | ||
if err != nil { | ||
fmt.Printf("Error creating new Smart Contract: %s", err) | ||
} | ||
} |
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,10 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const FabCar = require('./lib/fabcar'); | ||
|
||
module.exports.FabCar = FabCar; | ||
module.exports.contracts = [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,155 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const {Contract} = require('fabric-contract-api'); | ||
|
||
class FabCar extends Contract { | ||
|
||
async initLedger(ctx) { | ||
console.info('============= START : Initialize Ledger ==========='); | ||
const cars = [ | ||
{ | ||
color: 'blue', | ||
make: 'Toyota', | ||
model: 'Prius', | ||
owner: 'Tomoko', | ||
}, | ||
{ | ||
color: 'red', | ||
make: 'Ford', | ||
model: 'Mustang', | ||
owner: 'Brad', | ||
}, | ||
{ | ||
color: 'green', | ||
make: 'Hyundai', | ||
model: 'Tucson', | ||
owner: 'Jin Soo', | ||
}, | ||
{ | ||
color: 'yellow', | ||
make: 'Volkswagen', | ||
model: 'Passat', | ||
owner: 'Max', | ||
}, | ||
{ | ||
color: 'black', | ||
make: 'Tesla', | ||
model: 'S', | ||
owner: 'Adriana', | ||
}, | ||
{ | ||
color: 'purple', | ||
make: 'Peugeot', | ||
model: '205', | ||
owner: 'Michel', | ||
}, | ||
{ | ||
color: 'white', | ||
make: 'Chery', | ||
model: 'S22L', | ||
owner: 'Aarav', | ||
}, | ||
{ | ||
color: 'violet', | ||
make: 'Fiat', | ||
model: 'Punto', | ||
owner: 'Pari', | ||
}, | ||
{ | ||
color: 'indigo', | ||
make: 'Tata', | ||
model: 'Nano', | ||
owner: 'Valeria', | ||
}, | ||
{ | ||
color: 'brown', | ||
make: 'Holden', | ||
model: 'Barina', | ||
owner: 'Shotaro', | ||
}, | ||
]; | ||
|
||
for (let i = 0; i < cars.length; i++) { | ||
cars[i].docType = 'car'; | ||
await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i]))); | ||
console.info('Added <--> ', cars[i]); | ||
} | ||
console.info('============= END : Initialize Ledger ==========='); | ||
} | ||
|
||
async queryCar(ctx, carNumber) { | ||
const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state | ||
if (!carAsBytes || carAsBytes.length === 0) { | ||
throw new Error(`${carNumber} does not exist`); | ||
} | ||
console.log(carAsBytes.toString()); | ||
return carAsBytes.toString(); | ||
} | ||
|
||
async createCar(ctx, carNumber, make, model, color, owner) { | ||
console.info('============= START : Create Car ==========='); | ||
|
||
const car = { | ||
color, | ||
docType: 'car', | ||
make, | ||
model, | ||
owner, | ||
}; | ||
|
||
await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); | ||
console.info('============= END : Create Car ==========='); | ||
} | ||
|
||
async queryAllCars(ctx) { | ||
const startKey = 'CAR0'; | ||
const endKey = 'CAR999'; | ||
|
||
const iterator = await ctx.stub.getStateByRange(startKey, endKey); | ||
|
||
const allResults = []; | ||
let collect = true; | ||
while (collect) { | ||
const res = await iterator.next(); | ||
|
||
if (res.value && res.value.value.toString()) { | ||
|
||
const Key = res.value.key; | ||
let Record; | ||
try { | ||
Record = JSON.parse(res.value.value.toString('utf8')); | ||
} catch (err) { | ||
console.log(err); | ||
Record = res.value.value.toString('utf8'); | ||
} | ||
allResults.push({Key, Record}); | ||
} | ||
if (res.done) { | ||
await iterator.close(); | ||
collect = false; | ||
} | ||
} | ||
return JSON.stringify(allResults); | ||
} | ||
|
||
async changeCarOwner(ctx, carNumber, newOwner) { | ||
console.info('============= START : changeCarOwner ==========='); | ||
|
||
const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state | ||
if (!carAsBytes || carAsBytes.length === 0) { | ||
throw new Error(`${carNumber} does not exist`); | ||
} | ||
const car = JSON.parse(carAsBytes.toString()); | ||
car.owner = newOwner; | ||
|
||
await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); | ||
console.info('============= END : changeCarOwner ==========='); | ||
} | ||
|
||
} | ||
|
||
module.exports = FabCar; |
Empty file.
Oops, something went wrong.