mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
Add the javascript application for ledger queries
Add the asset-transfer-ledger-queries javascript application. Update the CI script to run it against go and javascript chaincode. Signed-off-by: Bret Harrison <beharrison@nc.rr.com>
This commit is contained in:
parent
8e2535ee65
commit
f361386231
12 changed files with 422 additions and 68 deletions
|
|
@ -9,7 +9,7 @@
|
|||
const {Gateway, Wallets} = require('fabric-network');
|
||||
const FabricCAServices = require('fabric-ca-client');
|
||||
const path = require('path');
|
||||
const {buildCAClient, registerUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js');
|
||||
const {buildCAClient, registerAndEnrollUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js');
|
||||
const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js');
|
||||
|
||||
const channelName = 'mychannel';
|
||||
|
|
@ -22,25 +22,46 @@ function prettyJSONString(inputString) {
|
|||
}
|
||||
|
||||
// pre-requisites:
|
||||
// - fabric-sample two organization test-network setup with two peers, ordering service, and 2 certificate authorities
|
||||
// - fabric-sample two organization test-network setup with two peers, ordering service,
|
||||
// and 2 certificate authorities
|
||||
// ===> from directory /fabric-samples/test-network
|
||||
// network.sh run createChannel -ca
|
||||
// - any of the asset-transfer-basic chaincodes deployed on the channel "mychannel" with the chaincodeName of "basic"
|
||||
// This deploy command will package, install, approve, and commit the javascript chaincode, all the actions it takes
|
||||
// ./network.sh up createChannel -ca
|
||||
// - Use any of the asset-transfer-basic chaincodes deployed on the channel "mychannel"
|
||||
// with the chaincode name of "basic". The following deploy command will package,
|
||||
// install, approve, and commit the javascript chaincode, all the actions it takes
|
||||
// to deploy a chaincode to a channel.
|
||||
// ===> from directory /fabric-samples/test-network
|
||||
// network.sh deployCC -ccn basic -ccl javascript
|
||||
// - node install
|
||||
// ./network.sh deployCC -ccn basic -ccl javascript
|
||||
// - Be sure that node.js is installed
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
|
||||
// node -v
|
||||
// - npm installed code dependencies
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
|
||||
// npm install
|
||||
// - to run this test application
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
|
||||
// node app.js
|
||||
// # this may be run again again
|
||||
|
||||
// NOTE: If you see kind an error like these:
|
||||
/*
|
||||
2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied
|
||||
******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied
|
||||
|
||||
OR
|
||||
|
||||
Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
|
||||
******** FAILED to run the application: Error: Identity not found in wallet: appUser
|
||||
*/
|
||||
// Delete the /fabric-samples/asset-transfer-basic/application-javascript/wallet directory
|
||||
// and retry this application.
|
||||
//
|
||||
// The certificate authority must have been restarted and the saved certificates for the
|
||||
// admin and application user are not valid. Deleting the wallet store will force these to be reset
|
||||
// with the new certificate authority.
|
||||
//
|
||||
|
||||
/**
|
||||
* A test application to show basic operations with any of the asset-transfer-basic chaincodes
|
||||
* A test application to show basic queries operations with any of the asset-transfer-basic chaincodes
|
||||
* -- How to submit a transaction
|
||||
* -- How to query and check the results
|
||||
*
|
||||
|
|
@ -64,7 +85,7 @@ async function main() {
|
|||
|
||||
// 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 registerUser(caClient, wallet, userId, 'org1.department1');
|
||||
await registerAndEnrollUser(caClient, wallet, userId, '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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "2.2.0",
|
||||
"fabric-network": "2.2.0"
|
||||
"fabric-ca-client": "^2.2.0",
|
||||
"fabric-network": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
coverage
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
mocha: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 8,
|
||||
sourceType: 'script'
|
||||
},
|
||||
extends: "eslint:recommended",
|
||||
rules: {
|
||||
indent: ['error', 'tab'],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
quotes: ['error', 'single'],
|
||||
semi: ['error', 'always'],
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-console': 'off',
|
||||
curly: 'error',
|
||||
eqeqeq: 'error',
|
||||
'no-throw-literal': 'error',
|
||||
strict: 'error',
|
||||
'no-var': 'error',
|
||||
'dot-notation': 'error',
|
||||
'no-trailing-spaces': 'error',
|
||||
'no-use-before-define': 'error',
|
||||
'no-useless-call': 'error',
|
||||
'no-with': 'error',
|
||||
'operator-linebreak': 'error',
|
||||
yoda: 'error',
|
||||
'quote-props': ['error', 'as-needed']
|
||||
}
|
||||
};
|
||||
14
asset-transfer-ledger-queries/application-javascript/.gitignore
vendored
Normal file
14
asset-transfer-ledger-queries/application-javascript/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
package-lock.json
|
||||
|
||||
wallet
|
||||
!wallet/.gitkeep
|
||||
242
asset-transfer-ledger-queries/application-javascript/app.js
Normal file
242
asset-transfer-ledger-queries/application-javascript/app.js
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright IBM Corp. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {Gateway, Wallets} = require('fabric-network');
|
||||
const FabricCAServices = require('fabric-ca-client');
|
||||
const path = require('path');
|
||||
const {buildCAClient, registerAndEnrollUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js');
|
||||
const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js');
|
||||
|
||||
const channelName = 'mychannel';
|
||||
const chaincodeName = 'ledger';
|
||||
const walletPath = path.join(__dirname, 'wallet');
|
||||
const userId = 'appUser';
|
||||
|
||||
function prettyJSONString(inputString) {
|
||||
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||
}
|
||||
|
||||
// pre-requisites:
|
||||
// - fabric-sample two organization test-network setup with two peers, ordering service,
|
||||
// and 2 certificate authorities, with the state database using couchdb
|
||||
// ===> from directory /fabric-samples/test-network
|
||||
// ./network.sh up createChannel -ca -s couchdb
|
||||
// - Use any of the asset-transfer-ledger-queries chaincodes deployed on the channel "mychannel"
|
||||
// with the chaincode name of "ledger". The following deploy command will package,
|
||||
// install, approve, and commit the javascript chaincode, all the actions it takes
|
||||
// to deploy a chaincode to a channel.
|
||||
// ===> from directory /fabric-samples/test-network
|
||||
// ./network.sh deployCC -ccn ledger -ccl javascript
|
||||
// - Be sure that node.js is installed
|
||||
// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
|
||||
// node -v
|
||||
// - npm installed code dependencies
|
||||
// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
|
||||
// npm install
|
||||
// - to run this test application
|
||||
// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
|
||||
// node app.js
|
||||
|
||||
// NOTE: If you see kind an error like these:
|
||||
/*
|
||||
2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied
|
||||
******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied
|
||||
|
||||
OR
|
||||
|
||||
Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
|
||||
******** FAILED to run the application: Error: Identity not found in wallet: appUser
|
||||
*/
|
||||
// Delete the /fabric-samples/asset-transfer-ledger-queries/application-javascript/wallet directory
|
||||
// and retry this application.
|
||||
//
|
||||
// The certificate authority must have been restarted and the saved certificates for the
|
||||
// admin and application user are not valid. Deleting the wallet store will force these to be reset
|
||||
// with the new certificate authority.
|
||||
//
|
||||
|
||||
/**
|
||||
* A test application to show ledger queries operations with any of the asset-transfer-ledger-queries chaincodes
|
||||
* -- How to submit a transaction
|
||||
* -- How to query and check the results
|
||||
*
|
||||
* To see the SDK workings, try setting the logging to show on the console before running
|
||||
* export HFC_LOGGING='{"debug":"console"}'
|
||||
*/
|
||||
async function main() {
|
||||
let skipInit = false;
|
||||
if (process.argv.length > 2) {
|
||||
if (process.argv[2] === 'skipInit') {
|
||||
skipInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// build an in memory object with the network configuration (also known as a connection profile)
|
||||
const ccp = buildCCP();
|
||||
|
||||
// build an instance of the fabric ca services client based on
|
||||
// the information in the network configuration
|
||||
const caClient = buildCAClient(FabricCAServices, ccp);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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, userId, '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();
|
||||
|
||||
try {
|
||||
// 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: userId,
|
||||
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.
|
||||
const contract = network.getContract(chaincodeName);
|
||||
|
||||
// Initialize a set of asset data on the channel using the chaincode 'InitLedger' function.
|
||||
// This type of transaction would only be run once by an application the first time it was started after it
|
||||
// deployed the first time. Any updates to the chaincode deployed later would likely not need to run
|
||||
// an "init" type function.
|
||||
if (!skipInit) {
|
||||
try {
|
||||
console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
|
||||
await contract.submitTransaction('InitLedger');
|
||||
console.log('*** Result: committed');
|
||||
} catch(initError) {
|
||||
// this is error is OK if we are rerunning this app without restarting
|
||||
console.log(`******** initLedger failed :: ${initError}`)
|
||||
}
|
||||
} else {
|
||||
console.log('*** not executing "InitLedger');
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
// Let's try a query operation (function).
|
||||
// This will be sent to just one peer and the results will be shown.
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRange, function returns assets in a specific range from asset1 to before asset6');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRange', 'asset1', 'asset6');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and open end range to return assest1 to asset6');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRange', '', '');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an fixed start (asset3) and open end range to return assest3 to asset6');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRange', 'asset3', '');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and fixed end (asset3) range to return assest1 to asset2');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRange', '', 'asset3');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Now let's try to submit a transaction.
|
||||
// This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
|
||||
// to the orderer to be committed by each of the peer's to the channel ledger.
|
||||
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID(asset7), color(yellow), size(5), owner(Tom), and appraisedValue(1300) arguments');
|
||||
await contract.submitTransaction('CreateAsset', 'asset7', 'yellow', '5', 'Tom', '1300');
|
||||
console.log('*** Result: committed');
|
||||
|
||||
console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset7)');
|
||||
result = await contract.evaluateTransaction('ReadAsset', 'asset7');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with ID(asset7) exist');
|
||||
result = await contract.evaluateTransaction('AssetExists', 'asset7');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Now let's try to submit a transaction that deletes an asset
|
||||
// This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
|
||||
// to the orderer to be committed by each of the peer's to the channel ledger.
|
||||
console.log('\n--> Submit Transaction: DeleteAsset with ID(asset7)');
|
||||
await contract.submitTransaction('DeleteAsset', 'asset7');
|
||||
console.log('*** Result: committed');
|
||||
|
||||
console.log('\n--> Evaluate Transaction: AssetExists, function returns "false" if an asset with ID(asset7) does not exist');
|
||||
result = await contract.evaluateTransaction('AssetExists', 'asset7');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`)
|
||||
|
||||
console.log('\n--> Submit Transaction: TransferAsset, transfer asset(asset2) to new owner(Tom)');
|
||||
await contract.submitTransaction('TransferAsset', 'asset2', 'Tom');
|
||||
console.log('*** Result: committed');
|
||||
|
||||
console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset2)');
|
||||
result = await contract.evaluateTransaction('ReadAsset', 'asset2');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Rich Query with Pagination (Only supported if CouchDB is used as state database)
|
||||
console.log('\n--> Evaluate Transaction: QueryAssetsWithPagination, function returns "Tom" assets');
|
||||
result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}','3','');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Submit Transaction: TransferAssetByColor, transfer all yellow assets to new owner(Michel)');
|
||||
await contract.submitTransaction('TransferAssetByColor', 'yellow', 'Michel');
|
||||
console.log('*** Result: committed');
|
||||
|
||||
// Rich Query (Only supported if CouchDB is used as state database):
|
||||
console.log('\n--> Evaluate Transaction: QueryAssetsByOwner, find all assets with owner(Michel)');
|
||||
result = await contract.evaluateTransaction('QueryAssetsByOwner', 'Michel');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Evaluate Transaction: GetAssetHistory, get the history of an asset(asset7)');
|
||||
result = await contract.evaluateTransaction('GetAssetHistory', 'asset7');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Rich Query (Only supported if CouchDB is used as state database):
|
||||
console.log('\n--> Evaluate Transaction: QueryAssets, assets of size 15');
|
||||
result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"size":15}}');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
|
||||
console.log('\n--> Evaluate Transaction: QueryAssets, Jin Soo\'s assets');
|
||||
result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"docType":"asset","owner":"Jin Soo"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Rich Query with Pagination (Only supported if CouchDB is used as state database)
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 1 of assets from asset3 to asset6 (asset3, asset4)');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', '');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Rich Query with Pagination (Only supported if CouchDB is used as state database)
|
||||
console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 2 of assets from asset3 to asset6 (asset4, asset5)');
|
||||
result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', 'asset4');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('*** all tests completed');
|
||||
} finally {
|
||||
// Disconnect from the gateway when the application is closing
|
||||
// This will close all connections to the network
|
||||
gateway.disconnect();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`******** FAILED to run the application: ${error}`);
|
||||
}
|
||||
|
||||
console.log('*** application ending');
|
||||
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "asset-transfer-basic",
|
||||
"version": "1.0.0",
|
||||
"description": "Asset-transfer-basic application implemented in JavaScript",
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.0",
|
||||
"fabric-network": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ type Asset struct {
|
|||
// HistoryQueryResult structure used for returning result of history query
|
||||
type HistoryQueryResult struct {
|
||||
Record *Asset `json:"record"`
|
||||
TxID string `json:"txID"`
|
||||
TxId string `json:"txId"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
IsDelete bool `json:"isDelete"`
|
||||
}
|
||||
|
|
@ -379,6 +379,8 @@ func getQueryResultForQueryStringWithPagination(ctx contractapi.TransactionConte
|
|||
|
||||
// GetAssetHistory returns the chain of custody for an asset since issuance.
|
||||
func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInterface, assetID string) ([]HistoryQueryResult, error) {
|
||||
log.Printf("GetAssetHistory: ID %v", assetID)
|
||||
|
||||
resultsIterator, err := ctx.GetStub().GetHistoryForKey(assetID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -393,17 +395,24 @@ func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInte
|
|||
}
|
||||
|
||||
var asset Asset
|
||||
err = json.Unmarshal(response.Value, &asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(response.Value) > 0 {
|
||||
err = json.Unmarshal(response.Value, &asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
asset = Asset{
|
||||
ID: assetID,
|
||||
}
|
||||
}
|
||||
|
||||
timestamp, err := ptypes.Timestamp(response.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := HistoryQueryResult{
|
||||
TxID: response.TxId,
|
||||
TxId: response.TxId,
|
||||
Timestamp: timestamp,
|
||||
Record: &asset,
|
||||
IsDelete: response.IsDelete,
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class Chaincode extends Contract{
|
|||
// ==== Create asset object and marshal to JSON ====
|
||||
let asset = {};
|
||||
asset.docType = 'asset';
|
||||
asset.assetID = assetID;
|
||||
asset.ID = assetID;
|
||||
asset.color = color;
|
||||
asset.size = size;
|
||||
asset.owner = owner;
|
||||
|
|
@ -90,7 +90,7 @@ class Chaincode extends Contract{
|
|||
// === Save asset to state ===
|
||||
await ctx.stub.putState(assetID, Buffer.from(JSON.stringify(asset)));
|
||||
let indexName = 'color~name'
|
||||
let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.assetID]);
|
||||
let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.ID]);
|
||||
|
||||
// Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
|
||||
// Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
|
||||
|
|
@ -137,7 +137,7 @@ class Chaincode extends Contract{
|
|||
|
||||
// delete the index
|
||||
let indexName = 'color~name';
|
||||
let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.assetID]);
|
||||
let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.ID]);
|
||||
if (!colorNameIndexKey) {
|
||||
throw new Error(' Failed to create the createCompositeKey');
|
||||
}
|
||||
|
|
@ -145,7 +145,7 @@ class Chaincode extends Contract{
|
|||
await ctx.stub.deleteState(colorNameIndexKey);
|
||||
}
|
||||
|
||||
// TransferAsset transfers a asset by setting a new owner name on the asset
|
||||
// TransferAsset transfers an asset by setting a new owner name on the asset
|
||||
async TransferAsset(ctx, assetName, newOwner) {
|
||||
|
||||
let assetAsBytes = await ctx.stub.getState(assetName);
|
||||
|
|
@ -245,7 +245,7 @@ class Chaincode extends Contract{
|
|||
// GetQueryResultForQueryString executes the passed in query string.
|
||||
// Result set is built and returned as a byte array containing the JSON results.
|
||||
async GetQueryResultForQueryString(ctx, queryString) {
|
||||
|
||||
|
||||
let resultsIterator = await ctx.stub.getQueryResult(queryString);
|
||||
let results = await this.GetAllResults(resultsIterator, false);
|
||||
|
||||
|
|
@ -258,7 +258,7 @@ class Chaincode extends Contract{
|
|||
// The number of fetched records will be equal to or lesser than the page size.
|
||||
// Paginated range queries are only valid for read only transactions.
|
||||
async GetAssetsByRangeWithPagination(ctx, startKey, endKey, pageSize, bookmark) {
|
||||
|
||||
|
||||
const { iterator, metadata } = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark);
|
||||
const results = await this.GetAllResults(iterator, false);
|
||||
|
||||
|
|
@ -293,8 +293,8 @@ class Chaincode extends Contract{
|
|||
// GetAssetHistory returns the chain of custody for an asset since issuance.
|
||||
async GetAssetHistory(ctx, assetName) {
|
||||
|
||||
let resultsIterator = await ctx.stub.getHistoryForKey(assetName);
|
||||
let results = await this.GetAllResults(resultsIterator, true);
|
||||
const resultsIterator = await ctx.stub.getHistoryForKey(assetName);
|
||||
const results = await this.GetAllResults(resultsIterator, true);
|
||||
|
||||
return JSON.stringify(results);
|
||||
}
|
||||
|
|
@ -302,7 +302,7 @@ class Chaincode extends Contract{
|
|||
// AssetExists returns true when asset with given ID exists in world state
|
||||
async AssetExists(ctx, assetName) {
|
||||
// ==== Check if asset already exists ====
|
||||
let assetState = await ctx.stub.getState(assetName);
|
||||
const assetState = await ctx.stub.getState(assetName);
|
||||
if ( !assetState || assetState.length === 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -310,80 +310,74 @@ class Chaincode extends Contract{
|
|||
}
|
||||
|
||||
async GetAllResults(iterator, isHistory) {
|
||||
let allResults = [];
|
||||
while (true) {
|
||||
let res = await iterator.next();
|
||||
const allResults = [];
|
||||
let res = await iterator.next();
|
||||
while (!res.done) {
|
||||
const jsonRes = {};
|
||||
|
||||
if (res.value && res.value.value.toString()) {
|
||||
let jsonRes = {};
|
||||
console.log(res.value.value.toString('utf8'));
|
||||
if (isHistory && isHistory === true) {
|
||||
jsonRes.TxId = res.value.tx_id;
|
||||
jsonRes.Timestamp = res.value.timestamp;
|
||||
try {
|
||||
jsonRes.Value = JSON.parse(res.value.value.toString('utf8'));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
jsonRes.Value = res.value.value.toString('utf8');
|
||||
}
|
||||
} else {
|
||||
jsonRes.Key = res.value.key;
|
||||
try {
|
||||
jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
jsonRes.Record = res.value.value.toString('utf8');
|
||||
}
|
||||
}
|
||||
allResults.push(jsonRes);
|
||||
if (isHistory) {
|
||||
jsonRes.TxId = res.value.txId;
|
||||
jsonRes.Timestamp = res.value.timestamp;
|
||||
jsonRes.IsDelete = res.value.isDelete;
|
||||
}
|
||||
if (res.done) {
|
||||
await iterator.close();
|
||||
return allResults;
|
||||
|
||||
if (res.value.value.length > 0) {
|
||||
jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
|
||||
} else {
|
||||
jsonRes.Record = {ID: res.value.key};
|
||||
}
|
||||
|
||||
jsonRes.Key = res.value.key;
|
||||
|
||||
console.log('Result: ' + JSON.stringify(jsonRes));
|
||||
allResults.push(jsonRes);
|
||||
res = await iterator.next();
|
||||
}
|
||||
|
||||
await iterator.close();
|
||||
return allResults;
|
||||
}
|
||||
|
||||
// InitLedger creates sample assets in the ledger
|
||||
async InitLedger(ctx) {
|
||||
const assets = [
|
||||
{
|
||||
assetID: 'asset1',
|
||||
ID: 'asset1',
|
||||
color: 'blue',
|
||||
size: 5,
|
||||
owner: 'Tom',
|
||||
appraisedValue: 100
|
||||
},
|
||||
{
|
||||
assetID: 'asset2',
|
||||
ID: 'asset2',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 100
|
||||
},
|
||||
{
|
||||
assetID: 'asset3',
|
||||
ID: 'asset3',
|
||||
color: 'green',
|
||||
size: 10,
|
||||
owner: 'Jin Soo',
|
||||
appraisedValue: 200
|
||||
},
|
||||
{
|
||||
assetID: 'asset4',
|
||||
ID: 'asset4',
|
||||
color: 'yellow',
|
||||
size: 10,
|
||||
owner: 'Max',
|
||||
appraisedValue: 200
|
||||
},
|
||||
{
|
||||
assetID: 'asset5',
|
||||
ID: 'asset5',
|
||||
color: 'black',
|
||||
size: 15,
|
||||
owner: 'Adriana',
|
||||
appraisedValue: 250
|
||||
},
|
||||
{
|
||||
assetID: 'asset6',
|
||||
ID: 'asset6',
|
||||
color: 'white',
|
||||
size: 15,
|
||||
owner: 'Michel',
|
||||
|
|
@ -394,7 +388,7 @@ class Chaincode extends Contract{
|
|||
for (let i = 0; i < assets.length; i++) {
|
||||
await this.CreateAsset(
|
||||
ctx,
|
||||
assets[i].assetID,
|
||||
assets[i].ID,
|
||||
assets[i].color,
|
||||
assets[i].size,
|
||||
assets[i].owner,
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@ popd
|
|||
|
||||
print "Stopping network"
|
||||
./network.sh down
|
||||
rm -R ../asset-transfer-basic/application-javascript/wallet
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
set -euo pipefail
|
||||
|
||||
FABRIC_VERSION=${FABRIC_VERSION:-2.2}
|
||||
CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go}
|
||||
CHAINCODE_NAME=${CHAINCODE_NAME:-ledger}
|
||||
|
||||
function print() {
|
||||
|
|
@ -14,8 +13,28 @@ function print() {
|
|||
print "Creating network"
|
||||
./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}"
|
||||
|
||||
print "Deploying ${CHAINCODE_NAME} chaincode"
|
||||
./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}"
|
||||
print "Deploying ${CHAINCODE_NAME} go chaincode"
|
||||
./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1.0 -ccs 1 -ccl go
|
||||
|
||||
# Run Javascript application against the go chaincode
|
||||
print "Initializing Javascript application"
|
||||
pushd ../asset-transfer-ledger-queries/application-javascript
|
||||
npm install
|
||||
print "Executing app.js"
|
||||
node app.js
|
||||
popd
|
||||
|
||||
print "Deploying ${CHAINCODE_NAME} javascript chaincode"
|
||||
./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 2.0 -ccs 2 -ccl javascript
|
||||
|
||||
# Run Javascript application against the javascript chaincode
|
||||
print "Initializing Javascript application"
|
||||
pushd ../asset-transfer-ledger-queries/application-javascript
|
||||
npm install
|
||||
print "Executing app.js"
|
||||
node app.js skipInit
|
||||
popd
|
||||
|
||||
print "Stopping network"
|
||||
./network.sh down
|
||||
rm -R ../asset-transfer-ledger-queries/application-javascript/wallet
|
||||
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const adminUserId = 'admin';
|
||||
const adminUserPasswd = 'adminpw';
|
||||
|
||||
|
|
@ -53,7 +50,7 @@ exports.enrollAdmin = async (caClient, wallet) => {
|
|||
}
|
||||
};
|
||||
|
||||
exports.registerUser = async (caClient, wallet, userId, affiliation) => {
|
||||
exports.registerAndEnrollUser = async (caClient, wallet, userId, affiliation) => {
|
||||
try {
|
||||
// Check to see if we've already enrolled the user
|
||||
const userIdentity = await wallet.get(userId);
|
||||
|
|
|
|||
Loading…
Reference in a new issue