diff --git a/supply-chain-client/AppUtil.js b/supply-chain-client/AppUtil.js new file mode 100644 index 00000000..bd104183 --- /dev/null +++ b/supply-chain-client/AppUtil.js @@ -0,0 +1,66 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +exports.buildCCPOrg1 = () => { + // load the common connection configuration file + const ccpPath = path.resolve(__dirname, '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const contents = fs.readFileSync(ccpPath, 'utf8'); + + // build a JSON object from the file contents + const ccp = JSON.parse(contents); + + console.log(`Loaded the network configuration located at ${ccpPath}`); + return ccp; +}; + +exports.buildCCPOrg2 = () => { + // load the common connection configuration file + const ccpPath = path.resolve(__dirname, '..', 'test-network', + 'organizations', 'peerOrganizations', 'org2.example.com', 'connection-org2.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const contents = fs.readFileSync(ccpPath, 'utf8'); + + // build a JSON object from the file contents + const ccp = JSON.parse(contents); + + console.log(`Loaded the network configuration located at ${ccpPath}`); + return ccp; +}; + +exports.buildWallet = async (Wallets, walletPath) => { + // Create a new wallet : Note that wallet is for managing identities. + let wallet; + if (walletPath) { + wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Built a file system wallet at ${walletPath}`); + } else { + wallet = await Wallets.newInMemoryWallet(); + console.log('Built an in memory wallet'); + } + + return wallet; +}; + +exports.prettyJSONString = (inputString) => { + if (inputString) { + return JSON.stringify(JSON.parse(inputString), null, 2); + } + else { + return inputString; + } +} diff --git a/supply-chain-client/CAUtil.js b/supply-chain-client/CAUtil.js new file mode 100644 index 00000000..10ec7344 --- /dev/null +++ b/supply-chain-client/CAUtil.js @@ -0,0 +1,98 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const adminUserId = 'admin'; +const adminUserPasswd = 'adminpw'; + +/** + * + * @param {*} FabricCAServices + * @param {*} ccp + */ +exports.buildCAClient = (FabricCAServices, ccp, caHostName) => { + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities[caHostName]; //lookup CA details from config + const caTLSCACerts = caInfo.tlsCACerts.pem; + const caClient = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + console.log(`Built a CA Client named ${caInfo.caName}`); + return caClient; +}; + +exports.enrollAdmin = async (caClient, wallet, orgMspId) => { + try { + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get(adminUserId); + if (identity) { + console.log('An identity for the admin user already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await caClient.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: orgMspId, + type: 'X.509', + }; + await wallet.put(adminUserId, x509Identity); + console.log('Successfully enrolled admin user and imported it into the wallet'); + } catch (error) { + console.error(`Failed to enroll admin user : ${error}`); + } +}; + +exports.registerAndEnrollUser = async (caClient, wallet, orgMspId, userId, affiliation) => { + try { + // Check to see if we've already enrolled the user + const userIdentity = await wallet.get(userId); + if (userIdentity) { + console.log(`An identity for the user ${userId} already exists in the wallet`); + return; + } + + // Must use an admin to register a new user + const adminIdentity = await wallet.get(adminUserId); + if (!adminIdentity) { + console.log('An identity for the admin user does not exist in the wallet'); + console.log('Enroll the admin user before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, adminUserId); + + // Register the user, enroll the user, and import the new identity into the wallet. + // if affiliation is specified by client, the affiliation value must be configured in CA + const secret = await caClient.register({ + affiliation: affiliation, + enrollmentID: userId, + role: 'client' + }, adminUser); + const enrollment = await caClient.enroll({ + enrollmentID: userId, + enrollmentSecret: secret + }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: orgMspId, + type: 'X.509', + }; + await wallet.put(userId, x509Identity); + console.log(`Successfully registered and enrolled user ${userId} and imported it into the wallet`); + } catch (error) { + console.error(`Failed to register user : ${error}`); + } +}; diff --git a/supply-chain-client/README.md b/supply-chain-client/README.md new file mode 100644 index 00000000..e57b9932 --- /dev/null +++ b/supply-chain-client/README.md @@ -0,0 +1,105 @@ +# farm-chain POC with Fabric v1.4 +--- + +## Setting up Hyperledger Fabric and Dependencies + +### Remove any pre-existing containers and images: + +``$ docker rm -f $(docker -q)`` \ +``$ docker rmi -f $(docker ps -aq)`` + +If you have not used docker previously on the same machine follow the step 1. + +1. Docker - Guide (https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04) +2. NodeJS - Guide (https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-18-04) +3. Hyperledger Fabric v1.4 and above + + + +### Setup Golang language: +```bash +curl -O https://storage.googleapis.com/golang/go1.11.1.linux-amd64.tar.gz +sha256sum go1.11.1.linux-amd64.tar.gz +tar -xvf go1.11.1.linux-amd64.tar.gz +sudo mv go /usr/local +``` +Add the following paths to the bashrc file +```bash +export GOPATH=$HOME/go +export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin +``` +Re-source the bash script +```bash +source ~/.bashrc +source ~/.bashrc +``` + +### Get fabric-samples and install +``` +git clone https://github.com/hyperledger/fabric-samples.git +``` +From the root level of the fabric-samples directory: +``` +$ ./scripts/bootstrap.h +``` + +## Start the Hyperledger Fabric network for the POC +From the root level of this project + +``` +$ ./startFabric.sh >&logs/startup_logs.txt +``` + +On first run of the project: install the required node js libraries, register the Admin and User components of our network, and start the server: + +``` +$ npm install +``` +If this throws some errors, try running ``$ npm update`` and then ``$npm install`` + +Enroll the admin user: +``` +$ node enrollAdmin.js +``` +Expected output: +```markdown +Store path:/home/sakya/.hfc-key-store +Successfully loaded admin from persistence +Assigned the admin user to the fabric client ::{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"091d15d647a3053a769faf8f4122e7ac577323b919d00413d6f4e2208337eee9","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICATCCAaigAwIBAgIUTyI7MAMMLUNlJt7m+dPVhKZKvXgwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTkwNTAzMDUyMTAwWhcNMjAwNTAyMDUy\nNjAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAE0i55Xns6VEn2Y+DUNgQR3bfbLz40B99srq5rKF+C\n8QH6A3lDMtN7dFJQvddZprSxNaScaA81sJzXmygJ/9qBzaNsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFNyNLIJjiV6+GK97W/DO7a30\nLbUbMCsGA1UdIwQkMCKAIEI5qg3NdtruuLoM2nAYUdFFBNMarRst3dusalc2Xkl8\nMAoGCCqGSM49BAMCA0cAMEQCIGe2ilQJ9PNaPueLFL9Joc9zaV7Eq0krEX1wBR8c\nWCE/AiBdDgWhzztwAtdsV7/y6NXkmvCcJQvgtmz/ga+7gcolIQ==\n-----END CERTIFICATE-----\n"}}} + +``` +Enroll the normal user: +``` +$ node enrollUser.js +``` +If you received the following error: ``Failed to register: Error: fabric-ca request register failed with errors [[{"code":20,"message":"Authentication failure"}]]`` + +remove the keys from hfc-key-store: ``rm -rf ~/.hfc-key-store/* +`` +After this, re-enroll the admin and normal user. Successful completion of ``$node enrollUser.js`` should give the following output: + +```markdown +Successfully loaded admin from persistence +Successfully registered user1 - secret:MPGQrXnJyxjc +Successfully enrolled member user "user1" +User1 was successfully registered and enrolled and is ready to intreact with the fabric network + +``` + + +*Start the server and browse to ``localhost:8000`` in order to interact with the POC network and ledger* +``` +$ node server.js >&logs/server_logs.txt +``` + +```markdown +NOTE: All console logs will be stored in the ``logs`` directory. +``` + +### Transaction flow in the network +![transaction flow diagram](transaction_flow.PNG) + +### Stakeholders in the network +1. Farmer -> invokes - recordProduce() +2. Buyer -> invokes - queryProduce() / changeProduceOwner() +3. Regulator -> invokes - queryProduce() / queryAllProduce() diff --git a/supply-chain-client/client/app.js b/supply-chain-client/client/app.js new file mode 100644 index 00000000..42e7d371 --- /dev/null +++ b/supply-chain-client/client/app.js @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +var app = angular.module('application', []); + +// Angular Controller +app.controller('appController', function($scope, appFactory){ + + $("#success_holder").hide(); + $("#success_create").hide(); + $("#error_holder").hide(); + $("#error_query").hide(); + + $scope.queryAllProduce = function(){ + + appFactory.queryAllProduce(function(data){ + $scope.all_produce = data; + }); + } + + $scope.queryProduce = function(){ + + var id = $scope.produce_id; + + appFactory.queryProduce(id, function(data){ + $scope.query_produce = data; + + if ($scope.query_produce == "Could not locate produce"){ + console.log() + $("#error_query").show(); + } else{ + $("#error_query").hide(); + } + }); + } + + $scope.recordProduce = function(){ + + appFactory.recordProduce($scope.produce, function(data){ + + $scope.create_produce = data; + $("#success_create").show(); + }); + } + + $scope.changeHolder = function(){ + + appFactory.changeHolder($scope.holder, function(data){ + $scope.change_holder = data; + if ($scope.change_holder == "Error: no produce found"){ + $("#error_holder").show(); + $("#success_holder").hide(); + } else{ + $("#success_holder").show(); + $("#error_holder").hide(); + } + }); + } + +}); + +// Angular Factory +app.factory('appFactory', function($http){ + + var factory = {}; + + factory.queryAllProduce = function(callback){ + + $http.get('/get_all_produce/').success(function(output){ + callback(output) + }); + } + + factory.queryProduce = function(id, callback){ + $http.get('/get_produce/'+id).success(function(output){ + callback(output) + }); + } + + factory.recordProduce = function(data, callback){ + console.log(data) + var produce = data.assetID + "-" + data.color + "-" + data.size + "-" + data.appraisedValue + "-" + data.owner; + + $http.get('/add_produce/'+produce).success(function(output){ + callback(output) + }); + } + + factory.changeHolder = function(data, callback){ + + var holder = data.assetID + "-" + data.name; + + $http.get('/change_holder/'+holder).success(function(output){ + callback(output) + }); + } + + return factory; +}); + + diff --git a/supply-chain-client/client/favicon.png b/supply-chain-client/client/favicon.png new file mode 100644 index 00000000..823bb5e9 Binary files /dev/null and b/supply-chain-client/client/favicon.png differ diff --git a/supply-chain-client/client/index.html b/supply-chain-client/client/index.html new file mode 100644 index 00000000..9e2e21e2 --- /dev/null +++ b/supply-chain-client/client/index.html @@ -0,0 +1,149 @@ + + + + + + Hyperledger Fabric v1.4 Transactions POC + + + + + + + + + + + + +
+
Hyperledger Fabric v1.4 Transactions POC
+
+ +
+
+ + + + +

+
+ + + + + + + + + + + + + + + + + + +
IDColorSizePriceOwner
{{produce.ID}}{{produce.Color}}{{produce.Size}}{{produce.AppraisedValue}}{{produce.Owner}}
+ +
+
+
Error: Please enter a valid Id
+ + Enter a ID number: + +
+ + + + + + + + + + + + + + + + + + +
IDColorSizePriceOwner
{{query_produce.ID}}{{query_produce.Color}}{{query_produce.Size}}{{query_produce.AppraisedValue}}{{query_produce.Owner}}
+ +
+ +
Successfully added to Ledger!
+
+ Fabric id: + Color: + Size: + Price: + Owner name: + +
+ +
+
+
Successfully updated Ledger! Tx ID: {{change_holder}}
+
Error: Please enter a valid Product Id
+ Enter a ID of the product to transfer ownership: + Enter name of new owner: + +
+ +
+ + + + \ No newline at end of file diff --git a/supply-chain-client/client/transaction_flow.PNG b/supply-chain-client/client/transaction_flow.PNG new file mode 100644 index 00000000..82622d89 Binary files /dev/null and b/supply-chain-client/client/transaction_flow.PNG differ diff --git a/supply-chain-client/enrollAdmin.js b/supply-chain-client/enrollAdmin.js new file mode 100644 index 00000000..5f24d9e0 --- /dev/null +++ b/supply-chain-client/enrollAdmin.js @@ -0,0 +1,73 @@ +'use strict'; +/* +* SPDX-License-Identifier: Apache-2.0 +*/ +/* + * Chaincode Invoke + */ + +var Fabric_Client = require('fabric-client'); +var Fabric_CA_Client = require('fabric-ca-client'); + +var path = require('path'); +var util = require('util'); +var os = require('os'); + +// +var fabric_client = new Fabric_Client(); +var fabric_ca_client = null; +var admin_user = null; +var member_user = null; +var store_path = path.join(os.homedir(), '.hfc-key-store'); +console.log(' Store path:'+store_path); + +// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting +Fabric_Client.newDefaultKeyValueStore({ path: store_path +}).then((state_store) => { + // assign the store to the fabric client + fabric_client.setStateStore(state_store); + var crypto_suite = Fabric_Client.newCryptoSuite(); + // use the same location for the state store (where the users' certificate are kept) + // and the crypto store (where the users' keys are kept) + var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path}); + crypto_suite.setCryptoKeyStore(crypto_store); + fabric_client.setCryptoSuite(crypto_suite); + var tlsOptions = { + trustedRoots: [], + verify: false + }; + // be sure to change the http to https when the CA is running TLS enabled + fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', tlsOptions , 'ca.example.com', crypto_suite); + + // first check to see if the admin is already enrolled + return fabric_client.getUserContext('admin', true); +}).then((user_from_store) => { + if (user_from_store && user_from_store.isEnrolled()) { + console.log('Successfully loaded admin from persistence'); + admin_user = user_from_store; + return null; + } else { + // need to enroll it with CA server + return fabric_ca_client.enroll({ + enrollmentID: 'admin', + enrollmentSecret: 'adminpw' + }).then((enrollment) => { + console.log('Successfully enrolled admin user "admin"'); + return fabric_client.createUser( + {username: 'admin', + mspid: 'Org1MSP', + cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate } + }); + }).then((user) => { + admin_user = user; + return fabric_client.setUserContext(admin_user); + }).catch((err) => { + console.error('Failed to enroll and persist admin. Error: ' + err.stack ? err.stack : err); + throw new Error('Failed to enroll admin'); + }); + } +}).then(() => { + console.log('Assigned the admin user to the fabric client ::' + admin_user.toString()); +}).catch((err) => { + console.error('Failed to enroll admin: ' + err); +}); \ No newline at end of file diff --git a/supply-chain-client/enrollUser.js b/supply-chain-client/enrollUser.js new file mode 100644 index 00000000..7d318610 --- /dev/null +++ b/supply-chain-client/enrollUser.js @@ -0,0 +1,84 @@ +'use strict'; +/* +* SPDX-License-Identifier: Apache-2.0 +*/ +/* + * Chaincode Invoke + +This code is based on code written by the Hyperledger Fabric community. + Original code can be found here: https://gerrit.hyperledger.org/r/#/c/14395/4/fabcar/registerUser.js + + */ + +var Fabric_Client = require('fabric-client'); +var Fabric_CA_Client = require('fabric-ca-client'); + +var path = require('path'); +var util = require('util'); +var os = require('os'); + +// +var fabric_client = new Fabric_Client(); +var fabric_ca_client = null; +var admin_user = null; +var member_user = null; +var store_path = path.join(os.homedir(), '.hfc-key-store'); +console.log(' Store path:'+store_path); + +// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting +Fabric_Client.newDefaultKeyValueStore({ path: store_path +}).then((state_store) => { + // assign the store to the fabric client + fabric_client.setStateStore(state_store); + var crypto_suite = Fabric_Client.newCryptoSuite(); + // use the same location for the state store (where the users' certificate are kept) + // and the crypto store (where the users' keys are kept) + var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path}); + crypto_suite.setCryptoKeyStore(crypto_store); + fabric_client.setCryptoSuite(crypto_suite); + var tlsOptions = { + trustedRoots: [], + verify: false + }; + // be sure to change the http to https when the CA is running TLS enabled + fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', null , '', crypto_suite); + + // first check to see if the admin is already enrolled + return fabric_client.getUserContext('admin', true); +}).then((user_from_store) => { + if (user_from_store && user_from_store.isEnrolled()) { + console.log('Successfully loaded admin from persistence'); + admin_user = user_from_store; + } else { + throw new Error('Failed to get admin.... run registerAdmin.js'); + } + + // at this point we should have the admin user + // first need to register the user with the CA server + return fabric_ca_client.register({enrollmentID: 'user1', affiliation: 'org1.department1'}, admin_user); +}).then((secret) => { + // next we need to enroll the user with CA server + console.log('Successfully registered user1 - secret:'+ secret); + + return fabric_ca_client.enroll({enrollmentID: 'user1', enrollmentSecret: secret}); +}).then((enrollment) => { + console.log('Successfully enrolled member user "user1" '); + return fabric_client.createUser( + {username: 'user1', + mspid: 'Org1MSP', + cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate } + }); +}).then((user) => { + member_user = user; + + return fabric_client.setUserContext(member_user); +}).then(()=>{ + console.log('User1 was successfully registered and enrolled and is ready to intreact with the fabric network'); + +}).catch((err) => { + console.error('Failed to register: ' + err); + if(err.toString().indexOf('Authorization') > -1) { + console.error('Authorization failures may be caused by having admin credentials from a previous CA instance.\n' + + 'Try again after deleting the contents of the store directory '+store_path); + } +}); \ No newline at end of file diff --git a/supply-chain-client/invoker.js b/supply-chain-client/invoker.js new file mode 100644 index 00000000..ef8204de --- /dev/null +++ b/supply-chain-client/invoker.js @@ -0,0 +1,104 @@ +const { Gateway, Wallets } = require('fabric-network'); +const FabricCAServices = require('fabric-ca-client'); +const path = require('path'); +const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('./CAUtil.js'); +const { buildCCPOrg1, buildWallet } = require('./AppUtil.js'); + +const channelName = 'mychannel'; +const chaincodeName = 'basic'; +const mspOrg1 = 'Org1MSP'; +const walletPath = path.join(__dirname, 'wallet'); +const org1UserId = 'appUser'; + +function prettyJSONString(inputString) { + return JSON.stringify(JSON.parse(inputString), null, 2); +} + +class FabricSampleService { + constructor(){ + + } + + async init() { + try { + const ccp = buildCCPOrg1(); + + // build an instance of the fabric ca services client based on + // the information in the network configuration + const caClient = buildCAClient(FabricCAServices, ccp, 'ca.org1.example.com'); + + // setup the wallet to hold the credentials of the application user + const wallet = await buildWallet(Wallets, walletPath); + + // in a real application this would be done on an administrative flow, and only once + await enrollAdmin(caClient, wallet, mspOrg1); + + // in a real application this would be done only when a new user was required to be added + // and would be part of an administrative flow + await registerAndEnrollUser(caClient, wallet, mspOrg1, org1UserId, 'org1.department1'); + + // Create a new gateway instance for interacting with the fabric network. + // In a real application this would be done as the backend server session is setup for + // a user that has been verified. + const gateway = new Gateway(); + + // setup the gateway instance + // The user will now be able to create connections to the fabric network and be able to + // submit transactions and query. All transactions submitted by this gateway will be + // signed by this user using the credentials stored in the wallet. + await gateway.connect(ccp, { + wallet, + identity: org1UserId, + discovery: { enabled: true, asLocalhost: true } // using asLocalhost as this gateway is using a fabric network deployed locally + }); + + // Build a network instance based on the channel where the smart contract is deployed + const network = await gateway.getNetwork(channelName); + + // Get the contract from the network. + this.contract = network.getContract(chaincodeName); + + console.log('Adding initial inventory to Ledger'); + await this.contract.submitTransaction('InitLedger'); + console.log('Done, applicaiton Ready!'); + + } catch (error) { + console.error(`******** FAILED to startup the FabicSampleService: ${error}`); + } + } + + async get_fabric(id) { + console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); + let result = await this.contract.evaluateTransaction('ReadAsset', id); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + return JSON.parse(result.toString()) + } + + async get_all_fabric() { + console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger'); + let result = await this.contract.evaluateTransaction('GetAllAssets'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + return JSON.parse(result.toString()) + } + + async add_fabric(fabric) { + console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); + let result = await this.contract.submitTransaction('CreateAsset', fabric.ID, fabric.Color, fabric.Size, fabric.Owner, fabric.AppraisedValue); + console.log('*** Result: committed'); + if (`${result}` !== '') { + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + return JSON.parse(result.toString()) + } + return {} + + } + + async change_owner(id, newowner) { + console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom'); + let result = await this.contract.submitTransaction('TransferAsset', id, newowner); + console.log(result.toString()) + return {} + } +} + +module.exports = FabricSampleService; \ No newline at end of file diff --git a/supply-chain-client/package.json b/supply-chain-client/package.json new file mode 100644 index 00000000..a4108c4c --- /dev/null +++ b/supply-chain-client/package.json @@ -0,0 +1,26 @@ +{ + "name": "Supply-Chain-app", + "version": "1.0.0", + "description": "Hyperledger Fabric POC", + "scripts": { + "start": "nodemon -L server.js" + }, + "dependencies": { + "angular": "^1.7.8", + "body-parser": "latest", + "ejs": "latest", + "express": "latest", + "fabric-ca-client": "^2.2.4", + "fabric-network": "^2.2.4" + }, + "devDependencies": { + "nodemon": "^2.0.15" + }, + "license": "Apache-2.0", + "keywords": [ + "Hyperledger", + "Fabric", + "Sample", + "Application" + ] +} diff --git a/supply-chain-client/routes.js b/supply-chain-client/routes.js new file mode 100644 index 00000000..e4938777 --- /dev/null +++ b/supply-chain-client/routes.js @@ -0,0 +1,27 @@ +const FabricSampleService = require("./invoker.js"); + +module.exports = async function(app){ + let fss = new FabricSampleService() + await fss.init() + + app.get('/get_produce/:id', async function(req, res){ + console.log(req.params) + asset = await fss.get_fabric(req.params.id); + + res.json(asset) + }); + app.get('/add_produce/:produce', async function(req, res){ + p = req.params.produce + pparts = p.split('-') + result = await fss.add_fabric({ID: pparts[0], Color: pparts[1], Size: pparts[2], AppraisedValue:pparts[3], Owner: pparts[4]}); + res.json(result) + }); + app.get('/get_all_produce', async function(req, res){ + res.json(await fss.get_all_fabric()) + }); + app.get('/change_holder/:holder', async function(req, res){ + p = req.params.holder + pparts = p.split('-') + res.json(await fss.change_owner(pparts[0], pparts[1])); + }); +} diff --git a/supply-chain-client/server.js b/supply-chain-client/server.js new file mode 100644 index 00000000..308b8a56 --- /dev/null +++ b/supply-chain-client/server.js @@ -0,0 +1,34 @@ +//SPDX-License-Identifier: Apache-2.0 + +// nodejs server setup + +// call the packages we need +var express = require('express'); // call express +var app = express(); // define our app using express +var bodyParser = require('body-parser'); +var path = require('path'); + +// Load all of our middleware +// configure app to use bodyParser() +// this will let us get the data from a POST +// app.use(express.static(__dirname + '/client')); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); + +// instantiate the app +var app = express(); + +// this line requires and runs the code from our routes.js file and passes it app +require('./routes.js')(app); + +// set up a static file server that points to the "client" directory +app.use(express.static(path.join(__dirname, './client'))); + +// Save our port +var port = process.env.PORT || 8000; + +// Start the server and listen on port +app.listen(port,function(){ + console.log("Server is now Live on port: " + port); +}); + diff --git a/supply-chain-client/transaction_flow.PNG b/supply-chain-client/transaction_flow.PNG new file mode 100644 index 00000000..82622d89 Binary files /dev/null and b/supply-chain-client/transaction_flow.PNG differ