+ 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