mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-23 01:55:10 +00:00
Added files
This commit is contained in:
parent
fd269e86bd
commit
ef5e91f82b
14 changed files with 868 additions and 0 deletions
66
supply-chain-client/AppUtil.js
Normal file
66
supply-chain-client/AppUtil.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
98
supply-chain-client/CAUtil.js
Normal file
98
supply-chain-client/CAUtil.js
Normal file
|
|
@ -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}`);
|
||||
}
|
||||
};
|
||||
105
supply-chain-client/README.md
Normal file
105
supply-chain-client/README.md
Normal file
|
|
@ -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
|
||||

|
||||
|
||||
### Stakeholders in the network
|
||||
1. Farmer -> invokes - recordProduce()
|
||||
2. Buyer -> invokes - queryProduce() / changeProduceOwner()
|
||||
3. Regulator -> invokes - queryProduce() / queryAllProduce()
|
||||
102
supply-chain-client/client/app.js
Normal file
102
supply-chain-client/client/app.js
Normal file
|
|
@ -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;
|
||||
});
|
||||
|
||||
|
||||
BIN
supply-chain-client/client/favicon.png
Normal file
BIN
supply-chain-client/client/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 896 B |
149
supply-chain-client/client/index.html
Normal file
149
supply-chain-client/client/index.html
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Hyperledger Fabric v1.4 Transactions POC</title>
|
||||
<link rel="icon" href="favicon.png" type="image/gif">
|
||||
|
||||
<!-- require jquery and bootstrap scripts -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
|
||||
|
||||
<!-- adding style here -->
|
||||
<style type="text/css">
|
||||
header{
|
||||
background-color: lightgray;
|
||||
font-size:20px;
|
||||
padding:15px;
|
||||
}
|
||||
header, .form-group{
|
||||
margin-bottom: 3%;
|
||||
}
|
||||
.form-group{
|
||||
width:50%;
|
||||
}
|
||||
#body{
|
||||
margin-left:3%;
|
||||
margin-right:3%;
|
||||
}
|
||||
.form-control{
|
||||
margin: 8px;
|
||||
}
|
||||
#right_header{
|
||||
width:20%;
|
||||
font-size:15px;
|
||||
margin-right:0px;
|
||||
}
|
||||
#left_header{
|
||||
margin-left:0;
|
||||
width:40%;
|
||||
display:inline-block;
|
||||
}
|
||||
#id {
|
||||
width:49%;
|
||||
display: inline-block;
|
||||
}
|
||||
table {
|
||||
font-family: arial, sans-serif;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
td, th {
|
||||
border: 1px solid #dddddd;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body ng-app="application" ng-controller="appController">
|
||||
<header>
|
||||
<div id="left_header">Hyperledger Fabric v1.4 Transactions POC</div>
|
||||
</header>
|
||||
|
||||
<div id="body">
|
||||
<div class="form-group">
|
||||
<a href="transaction_flow.PNG" target="_blank">
|
||||
<img src="transaction_flow.PNG" style="width:100%">
|
||||
</a>
|
||||
<label>List All Fabric items in the Ledger</label>
|
||||
<p><input id="queryAllProduce" type="submit" value="Query" class="btn btn-primary" ng-click="queryAllProduce()"></p>
|
||||
</div>
|
||||
|
||||
<table id="all_produce" class="table" align="center">
|
||||
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Color</th>
|
||||
<th>Size</th>
|
||||
<th>Price</th>
|
||||
<th>Owner</th>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="produce in all_produce">
|
||||
<td>{{produce.ID}}</td>
|
||||
<td>{{produce.Color}}</td>
|
||||
<td>{{produce.Size}}</td>
|
||||
<td>{{produce.AppraisedValue}}</td>
|
||||
<td>{{produce.Owner}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Query a Specific Fabric by ID</label><br>
|
||||
<h5 style="color:red;margin-bottom:2%" id="error_query">Error: Please enter a valid Id</h5>
|
||||
|
||||
Enter a ID number: <input id="createName" class="form-control" type="text" placeholder="Ex: 3" ng-model="produce_id">
|
||||
<input id="querySubmit" type="submit" value="Query" class="btn btn-primary" ng-click="queryProduce()">
|
||||
</div>
|
||||
|
||||
<table id="query_produce" class="table" align="center">
|
||||
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Color</th>
|
||||
<th>Size</th>
|
||||
<th>Price</th>
|
||||
<th>Owner</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{{query_produce.ID}}</td>
|
||||
<td>{{query_produce.Color}}</td>
|
||||
<td>{{query_produce.Size}}</td>
|
||||
<td>{{query_produce.AppraisedValue}}</td>
|
||||
<td>{{query_produce.Owner}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Create Produce Record</label>
|
||||
<h5 style="color:green;margin-bottom:2%" id="success_create">Successfully added to Ledger!</h5>
|
||||
<br>
|
||||
Fabric id: <input class="form-control" type="text" placeholder="Ex: 6" ng-model="produce.assetID">
|
||||
Color: <input class="form-control" type=text placeholder="Ex: blue" ng-model="produce.color">
|
||||
Size: <input class="form-control" type=number step=1 ng-model="produce.size">
|
||||
Price: <input class="form-control" type=number step=1 ng-model="produce.appraisedValue">
|
||||
Owner name: <input class="form-control" type="text" placeholder="Ex: Sakya" ng-model="produce.owner">
|
||||
<input id="createSubmit" type="submit" value="Create" class="btn btn-primary" ng-click="recordProduce()">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Change Product Owner</label><br>
|
||||
<h5 style="color:green;margin-bottom:2%" id="success_holder">Successfully updated Ledger! Tx ID: {{change_holder}}</h5>
|
||||
<h5 style="color:red;margin-bottom:2%" id="error_holder">Error: Please enter a valid Product Id</h5>
|
||||
Enter a ID of the product to transfer ownership: <input class="form-control" name="assetSelect" placeholder="Ex: 1" ng-model="holder.assetID">
|
||||
Enter name of new owner: <input class="form-control" name="assetSelect" placeholder="Ex: Dalio" ng-model="holder.name">
|
||||
<input id="transferSubmit" type="submit" value="Change" class="btn btn-primary" ng-click="changeHolder()">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
<!-- requiring the angular page -->
|
||||
<script type="text/javascript" src="app.js"> </script>
|
||||
</html>
|
||||
BIN
supply-chain-client/client/transaction_flow.PNG
Normal file
BIN
supply-chain-client/client/transaction_flow.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
73
supply-chain-client/enrollAdmin.js
Normal file
73
supply-chain-client/enrollAdmin.js
Normal file
|
|
@ -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);
|
||||
});
|
||||
84
supply-chain-client/enrollUser.js
Normal file
84
supply-chain-client/enrollUser.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
104
supply-chain-client/invoker.js
Normal file
104
supply-chain-client/invoker.js
Normal file
|
|
@ -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;
|
||||
26
supply-chain-client/package.json
Normal file
26
supply-chain-client/package.json
Normal file
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
27
supply-chain-client/routes.js
Normal file
27
supply-chain-client/routes.js
Normal file
|
|
@ -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]));
|
||||
});
|
||||
}
|
||||
34
supply-chain-client/server.js
Normal file
34
supply-chain-client/server.js
Normal file
|
|
@ -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);
|
||||
});
|
||||
|
||||
BIN
supply-chain-client/transaction_flow.PNG
Normal file
BIN
supply-chain-client/transaction_flow.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Loading…
Reference in a new issue