Basic working version

This commit is contained in:
jandammans 2022-07-26 13:18:17 +02:00
parent 41717d76f2
commit ccc1045468
9 changed files with 410 additions and 165 deletions

View file

@ -4,110 +4,44 @@
import { Context, Contract } from 'fabric-contract-api';
import { Car } from './car';
import {Ruleset} from './ruleset';
import {Validation} from './validation';
export class FabCar extends Contract {
public async initLedger(ctx: Context) {
console.info('============= START : Initialize Ledger ===========');
const cars: Car[] = [
public currentContractNumber = 0;
public async validateAge(ctx: Context, age: string) {
const rulesets: Ruleset[] = [
{
color: 'blue',
make: 'Toyota',
model: 'Prius',
owner: 'Tomoko',
minimumAge: 16,
name: 'First ruleset',
},
{
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',
minimumAge: 18,
name: 'Second ruleset',
},
];
const validation: Validation = new Validation();
validation.id = this.getNewId();
const minimumAge: number = Math.max(rulesets[0].minimumAge, rulesets[1].minimumAge);
const ageToValidate = Number(age);
// Validate age
validation.isValid = (minimumAge <= ageToValidate);
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]);
}
await ctx.stub.putState('VALIDATION' + validation.id, Buffer.from(JSON.stringify(validation.toString())));
console.log('Added <--> ', validation);
}
public async initLedger(ctx: Context) {
console.log('============= START : Initialize Ledger ===========');
console.info('============= END : Initialize Ledger ===========');
}
public async queryCar(ctx: Context, carNumber: string): Promise<string> {
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();
}
public async createCar(ctx: Context, carNumber: string, make: string, model: string, color: string, owner: string) {
console.info('============= START : Create Car ===========');
const car: Car = {
color,
docType: 'car',
make,
model,
owner,
};
await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
console.info('============= END : Create Car ===========');
}
public async queryAllCars(ctx: Context): Promise<string> {
public async queryAllValidations(ctx: Context): Promise<string> {
const startKey = '';
const endKey = '';
const allResults = [];
for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {
const strValue = Buffer.from(value).toString('utf8');
let record;
@ -122,19 +56,17 @@ export class FabCar extends Contract {
console.info(allResults);
return JSON.stringify(allResults);
}
public async changeCarOwner(ctx: Context, carNumber: string, newOwner: string) {
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`);
public async getValidationById(ctx: Context, validationId: string): Promise<string> {
const validationrAsBytes = await ctx.stub.getState(validationId); // get the validation from chaincode state
if (!validationrAsBytes || validationrAsBytes.length === 0) {
throw new Error(`${validationId} does not exist`);
}
const car: Car = JSON.parse(carAsBytes.toString());
car.owner = newOwner;
await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
console.info('============= END : changeCarOwner ===========');
console.log(validationrAsBytes.toString());
return validationrAsBytes.toString();
}
private getNewId(): number {
this.currentContractNumber++;
return this.currentContractNumber;
}
}

View file

@ -2,7 +2,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { FabCar } from './fabcar';
export { FabCar } from './fabcar';
import {FabCar} from './fabcar';
export const contracts: any[] = [ FabCar ];

View file

@ -0,0 +1,4 @@
export class Ruleset {
public minimumAge: number;
public name: string;
}

View file

@ -0,0 +1,8 @@
export class Validation {
public executeDate: Date = new Date();
public id: number;
public isValid: boolean;
public toString(): string {
return '{id: ' + this.id + '; executeDate: ' + this.executeDate + '; isValid: ' + this.isValid + '}' ;
}
}

View file

@ -10,7 +10,7 @@ set -e
# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1
starttime=$(date +%s)
CC_SRC_LANGUAGE=${1:-"go"}
CC_SRC_LANGUAGE=${1:-"typescript"}
CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]`
if [ "$CC_SRC_LANGUAGE" = "go" -o "$CC_SRC_LANGUAGE" = "golang" ] ; then
@ -45,30 +45,6 @@ cat <<EOF
Total setup execution time : $(($(date +%s) - starttime)) secs ...
Next, use the FabCar applications to interact with the deployed FabCar contract.
The FabCar applications are available in multiple programming languages.
Follow the instructions for the programming language of your choice:
JavaScript:
Start by changing into the "javascript" directory:
cd javascript
Next, install all required packages:
npm install
Then run the following applications to enroll the admin user, and register a new user
called appUser which will be used by the other applications to interact with the deployed
FabCar contract:
node enrollAdmin
node registerUser
You can run the invoke application as follows. By default, the invoke application will
create a new car, but you can update the application to submit other transactions:
node invoke
You can run the query application as follows. By default, the query application will
return all cars, but you can update the application to evaluate other transactions:
node query
TypeScript:
@ -95,34 +71,4 @@ TypeScript:
return all cars, but you can update the application to evaluate other transactions:
node dist/query
Java:
Start by changing into the "java" directory:
cd java
Then, install dependencies and run the test using:
mvn test
The test will invoke the sample client app which perform the following:
- Enroll admin and appUser and import them into the wallet (if they don't already exist there)
- Submit a transaction to create a new car
- Evaluate a transaction (query) to return details of this car
- Submit a transaction to change the owner of this car
- Evaluate a transaction (query) to return the updated details of this car
Go:
Start by changing into the "go" directory:
cd go
Then, install dependencies and run the test using:
go run fabcar.go
The test will invoke the sample client app which perform the following:
- Import user credentials into the wallet (if they don't already exist there)
- Submit a transaction to create a new car
- Evaluate a transaction (query) to return details of this car
- Submit a transaction to change the owner of this car
- Evaluate a transaction (query) to return the updated details of this car
EOF

View file

@ -10,7 +10,7 @@ import * as path from 'path';
async function main() {
try {
// load the network configuration
const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
// Create a new CA client for interacting with the CA.

View file

@ -0,0 +1,50 @@
/*
* SPDX-License-Identifier: Apache-2.0
*/
import { Gateway, Wallets } from 'fabric-network';
import * as fs from 'fs';
import * as path from 'path';
async function main() {
try {
// load the network configuration
const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
// Create a new file system based wallet for managing identities.
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = await Wallets.newFileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
// Check to see if we've already enrolled the user.
const identity = await wallet.get('appUser');
if (!identity) {
console.log('An identity for the user "appUser" does not exist in the wallet');
console.log('Run the registerUser.ts application before retrying');
return;
}
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('DJINN');
// Evaluate the specified transaction.
// queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4')
// queryAllCars transaction - requires no arguments, ex: ('queryAllCars')
const result = await contract.evaluateTransaction('getValidationById', 'VALIDATION2');
console.log(`Transaction has been evaluated, result is: ${result.toString()}`);
} catch (error) {
console.error(`Failed to evaluate transaction: ${error}`);
process.exit(1);
}
}
main();

View file

@ -2,16 +2,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { Wallets, X509Identity } from 'fabric-network';
import * as FabricCAServices from 'fabric-ca-client';
import * as path from 'path';
import { Wallets, X509Identity } from 'fabric-network';
import * as fs from 'fs';
import * as path from 'path';
async function main() {
try {
// load the network configuration
const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
// Create a new CA client for interacting with the CA.
const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
@ -38,8 +38,8 @@ async function main() {
}
// build a user object for authenticating with the CA
const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
const adminUser = await provider.getUserContext(adminIdentity, 'admin');
const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
const adminUser = await provider.getUserContext(adminIdentity, 'admin');
// Register the user, enroll the user, and import the new identity into the wallet.
const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser);

306
install-fabric.sh Executable file
View file

@ -0,0 +1,306 @@
#!/bin/bash
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
# A modified version of the Fabric bootstrap script
# Use positional arguments to select componenets to install
#
# Has exactly the same functional power of bootstrap.sh
### START OF CODE GENERATED BY Argbash v2.9.0 ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
# Generated online by https://argbash.io/generate
# Default values
_positionals=()
_arg_comp=('' )
# if version not passed in, default to latest released version
# if ca version not passed in, default to latest released version
_arg_fabric_version="2.4.4"
_arg_ca_version="1.5.5"
ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m |sed 's/x86_64/amd64/g')" |sed 's/darwin-arm64/darwin-amd64/g')
MARCH=$(uname -m)
die()
{
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
echo "$1" >&2
exit "${_ret}"
}
begins_with_short_option()
{
local first_option all_short_options='fc'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
print_help()
{
printf 'Usage: %s [-f|--fabric-version <arg>] [-c|--ca-version <arg>] <comp-1> [<comp-2>] ... [<comp-n>] ...\n' "$0"
printf '\t%s\n' "<comp> Component to install, one or more of docker | binary | samples | podman First letter of component also accepted; If none specified docker | binary | samples is assumed"
printf '\t%s\n' "-f, --fabric-version: FabricVersion (default: '2.4.4')"
printf '\t%s\n' "-c, --ca-version: Fabric CA Version (default: '1.5.5')"
}
parse_commandline()
{
_positionals_count=0
while test $# -gt 0
do
_key="$1"
case "$_key" in
-f|--fabric-version)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_fabric_version="$2"
shift
;;
--fabric-version=*)
_arg_fabric_version="${_key##--fabric-version=}"
;;
-f*)
_arg_fabric_version="${_key##-f}"
;;
-c|--ca-version)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_ca_version="$2"
shift
;;
-h|--help)
print_help
exit 0
;;
-h*)
print_help
exit 0
;;
--ca-version=*)
_arg_ca_version="${_key##--ca-version=}"
;;
-c*)
_arg_ca_version="${_key##-c}"
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
_positionals_count=$((_positionals_count + 1))
;;
esac
shift
done
}
handle_passed_args_count()
{
local _required_args_string="'comp'"
# test "${_positionals_count}" -ge 1 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require at least 1 (namely: $_required_args_string), but got only ${_positionals_count}." 1
}
assign_positional_args()
{
local _positional_name _shift_for=$1
_positional_names="_arg_comp "
_our_args=$((${#_positionals[@]} - 1))
for ((ii = 0; ii < _our_args; ii++))
do
_positional_names="$_positional_names _arg_comp[$((ii + 1))]"
done
shift "$_shift_for"
for _positional_name in ${_positional_names}
do
test $# -gt 0 || break
eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
shift
done
}
# End of ARGBASH code
# dockerPull() pulls docker images from fabric and chaincode repositories
# note, if a docker image doesn't exist for a requested release, it will simply
# be skipped, since this script doesn't terminate upon errors.
singleImagePull() {
#three_digit_image_tag is passed in, e.g. "1.4.7"
three_digit_image_tag=$1
shift
#two_digit_image_tag is derived, e.g. "1.4", especially useful as a local tag for two digit references to most recent baseos, ccenv, javaenv, nodeenv patch releases
two_digit_image_tag=$(echo "$three_digit_image_tag" | cut -d'.' -f1,2)
while [[ $# -gt 0 ]]
do
image_name="$1"
echo "====> hyperledger/fabric-$image_name:$three_digit_image_tag"
${CONTAINER_CLI} pull "hyperledger/fabric-$image_name:$three_digit_image_tag"
${CONTAINER_CLI} tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name"
${CONTAINER_CLI} tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name:$two_digit_image_tag"
shift
done
}
cloneSamplesRepo() {
# clone (if needed) hyperledger/fabric-samples and checkout corresponding
# version to the binaries and docker images to be downloaded
if [ -d test-network ]; then
# if we are in the fabric-samples repo, checkout corresponding version
echo "==> Already in fabric-samples repo"
elif [ -d fabric-samples ]; then
# if fabric-samples repo already cloned and in current directory,
# cd fabric-samples
echo "===> Changing directory to fabric-samples"
cd fabric-samples
else
echo "===> Cloning hyperledger/fabric-samples repo"
git clone -b main https://github.com/hyperledger/fabric-samples.git && cd fabric-samples
fi
if GIT_DIR=.git git rev-parse v${VERSION} >/dev/null 2>&1; then
echo "===> Checking out v${VERSION} of hyperledger/fabric-samples"
git checkout -q v${VERSION}
else
echo "fabric-samples v${VERSION} does not exist, defaulting to main. fabric-samples main branch is intended to work with recent versions of fabric."
git checkout -q main
fi
}
# This will download the .tar.gz
download() {
local BINARY_FILE=$1
local URL=$2
local DEST_DIR=$(pwd)
echo "===> Downloading: " "${URL}"
if [ -d fabric-samples ]; then
DEST_DIR="fabric-samples"
fi
echo "===> Will unpack to: ${DEST_DIR}"
curl -L --retry 5 --retry-delay 3 "${URL}" | tar xz -C ${DEST_DIR}|| rc=$?
if [ -n "$rc" ]; then
echo "==> There was an error downloading the binary file."
return 22
else
echo "==> Done."
fi
}
pullBinaries() {
echo "===> Downloading version ${FABRIC_TAG} platform specific fabric binaries"
download "${BINARY_FILE}" "https://github.com/hyperledger/fabric/releases/download/v${VERSION}/${BINARY_FILE}"
if [ $? -eq 22 ]; then
echo
echo "------> ${FABRIC_TAG} platform specific fabric binary is not available to download <----"
echo
exit
fi
echo "===> Downloading version ${CA_TAG} platform specific fabric-ca-client binary"
download "${CA_BINARY_FILE}" "https://github.com/hyperledger/fabric-ca/releases/download/v${CA_VERSION}/${CA_BINARY_FILE}"
if [ $? -eq 22 ]; then
echo
echo "------> ${CA_TAG} fabric-ca-client binary is not available to download (Available from 1.1.0-rc1) <----"
echo
exit
fi
}
pullImages() {
command -v ${CONTAINER_CLI} >& /dev/null
NODOCKER=$?
if [ "${NODOCKER}" == 0 ]; then
FABRIC_IMAGES=(peer orderer ccenv tools)
case "$VERSION" in
2.*)
FABRIC_IMAGES+=(baseos)
shift
;;
esac
echo "FABRIC_IMAGES:" "${FABRIC_IMAGES[@]}"
echo "===> Pulling fabric Images"
singleImagePull "${FABRIC_TAG}" "${FABRIC_IMAGES[@]}"
echo "===> Pulling fabric ca Image"
CA_IMAGE=(ca)
singleImagePull "${CA_TAG}" "${CA_IMAGE[@]}"
echo "===> List out hyperledger images"
${CONTAINER_CLI} images | grep hyperledger
else
echo "========================================================="
echo "${CONTAINER_CLI} not installed, bypassing download of Fabric images"
echo "========================================================="
fi
}
# Main code starts here
parse_commandline "$@"
handle_passed_args_count
assign_positional_args 1 "${_positionals[@]}"
VERSION=$_arg_fabric_version
CA_VERSION=$_arg_ca_version
# prior to 1.2.0 architecture was determined by uname -m
if [[ $VERSION =~ ^1\.[0-1]\.* ]]; then
export FABRIC_TAG=${MARCH}-${VERSION}
export CA_TAG=${MARCH}-${CA_VERSION}
else
# starting with 1.2.0, multi-arch images will be default
: "${CA_TAG:="$CA_VERSION"}"
: "${FABRIC_TAG:="$VERSION"}"
fi
BINARY_FILE=hyperledger-fabric-${ARCH}-${VERSION}.tar.gz
CA_BINARY_FILE=hyperledger-fabric-ca-${ARCH}-${CA_VERSION}.tar.gz
# if nothing has been specified, assume everything
if [[ ${_arg_comp[@]} =~ ^$ ]]; then
echo "No options selected: Getting all samples, binaries, and docker images"
echo "Abort now if not the intention"
sleep 3 # just to give a chance to abort if this wasn't intended
_arg_comp=('samples','binary','docker')
fi
# Process samples first then the binaries. So if the fabric-samples dir is present
# the binaries will go there
if [[ "${_arg_comp[@]}" =~ (^| |,)s(amples)? ]]; then
echo
echo "Clone hyperledger/fabric-samples repo"
echo
cloneSamplesRepo
fi
if [[ "${_arg_comp[@]}" =~ (^| |,)b(inary)? ]]; then
echo
echo "Pull Hyperledger Fabric binaries"
echo
pullBinaries
fi
if [[ "${_arg_comp[@]}" =~ (^| |,)p(odman)? ]]; then
echo
echo "Pull Hyperledger Fabric podman images"
echo
CONTAINER_CLI=podman
pullImages
fi
if [[ "${_arg_comp[@]}" =~ (^| |,)d(ocker)? ]]; then
echo
echo "Pull Hyperledger Fabric docker images"
echo
CONTAINER_CLI=docker
pullImages
fi