From 11c05fa6122a4fe4be330b6249ee39af9033d4b0 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Mon, 10 Aug 2020 23:43:38 -0400 Subject: [PATCH 1/9] Add missing apps and chaincodes to CI Adds the apps and chaincodes to linting and testing CI that weren't added before. Linting issues were corrected where necessary to make CI pass. The Basic-Go application and Private-Javascript application are currently disabled pending fixes currently being worked on. Signed-off-by: Brett Logan --- .../application-go/assetTransfer.go | 68 +-- .../chaincode-javascript/.eslintignore | 5 + .../chaincode-javascript/.eslintrc.js | 37 ++ .../lib/asset_transfer_ledger_chaincode.js | 575 +++++++++--------- .../chaincode-typescript/src/asset.ts | 4 +- .../chaincode-typescript/src/assetContract.ts | 14 +- ci/azure-pipelines.yml | 48 +- ci/scripts/run-test-network-basic.sh | 38 +- ci/scripts/run-test-network-ledger.sh | 43 +- ci/scripts/run-test-network-private.sh | 31 +- ci/scripts/run-test-network-sbe.sh | 28 + ci/scripts/run-test-network-secured.sh | 19 +- 12 files changed, 525 insertions(+), 385 deletions(-) create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/.eslintignore create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js create mode 100755 ci/scripts/run-test-network-sbe.sh diff --git a/asset-transfer-basic/application-go/assetTransfer.go b/asset-transfer-basic/application-go/assetTransfer.go index 8bfa5f04..a8873281 100644 --- a/asset-transfer-basic/application-go/assetTransfer.go +++ b/asset-transfer-basic/application-go/assetTransfer.go @@ -7,9 +7,9 @@ SPDX-License-Identifier: Apache-2.0 package main import ( - "errors" "fmt" "io/ioutil" + "log" "os" "path/filepath" @@ -18,20 +18,22 @@ import ( ) func main() { - fmt.Println("============ application-golang starts ============") + log.Println("============ application-golang starts ============") + + err := os.Setenv("DISCOVERY_AS_LOCALHOST", "true") + if err != nil { + log.Fatalf("Error setting DISCOVERY_AS_LOCALHOST environemnt variable: %v", err) + } - os.Setenv("DISCOVERY_AS_LOCALHOST", "true") wallet, err := gateway.NewFileSystemWallet("wallet") if err != nil { - fmt.Printf("failed to create wallet: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to create wallet: %v", err) } if !wallet.Exists("appUser") { err = populateWallet(wallet) if err != nil { - fmt.Printf("failed to populate wallet contents: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to populate wallet contents: %v", err) } } @@ -50,57 +52,56 @@ func main() { gateway.WithIdentity(wallet, "appUser"), ) if err != nil { - fmt.Printf("failed to connect to gateway: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to connect to gateway: %v", err) } defer gw.Close() network, err := gw.GetNetwork("mychannel") if err != nil { - fmt.Printf("failed to get network: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to get network: %v", err) } contract := network.GetContract("basic") - result, err := contract.EvaluateTransaction("GetAllAssets") + result, err := contract.SubmitTransaction("InitLedger") if err != nil { - fmt.Printf("failed to evaluate transaction: %v\n", err) - os.Exit(1) + log.Fatalf("failed to evaluate transaction: %v", err) } - fmt.Println(string(result)) + log.Println(string(result)) - result, err = contract.SubmitTransaction("CreateAsset", "asset13", "yellow", "Tom", "5", "1300") + result, err = contract.EvaluateTransaction("GetAllAssets") if err != nil { - fmt.Printf("failed to submit transaction: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to evaluate transaction: %v", err) } - fmt.Println(string(result)) + log.Println(string(result)) + + result, err = contract.SubmitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300") + if err != nil { + log.Fatalf("Failed to submit transaction: %v", err) + } + log.Println(string(result)) result, err = contract.EvaluateTransaction("ReadAsset", "asset4") if err != nil { - fmt.Printf("failed to evaluate transaction: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to evaluate transaction: %v", err) } - fmt.Println(string(result)) + log.Println(string(result)) _, err = contract.SubmitTransaction("TransferAsset", "asset1", "Tom") if err != nil { - fmt.Printf("Failed to submit transaction: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to submit transaction: %v", err) } result, err = contract.EvaluateTransaction("ReadAsset", "asset1") if err != nil { - fmt.Printf("failed to evaluate transaction: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to evaluate transaction: %v", err) } - fmt.Println(string(result)) - fmt.Println("============ application-golang ends ============") + log.Println(string(result)) + log.Println("============ application-golang ends ============") } func populateWallet(wallet *gateway.Wallet) error { - fmt.Println("============ populate wallet starts ============") + log.Println("============ Populating wallet ============") credPath := filepath.Join( "..", "..", @@ -127,7 +128,7 @@ func populateWallet(wallet *gateway.Wallet) error { return err } if len(files) != 1 { - return errors.New("keystore folder should have contain one file") + return fmt.Errorf("keystore folder should have contain one file") } keyPath := filepath.Join(keyDir, files[0].Name()) key, err := ioutil.ReadFile(filepath.Clean(keyPath)) @@ -137,10 +138,5 @@ func populateWallet(wallet *gateway.Wallet) error { identity := gateway.NewX509Identity("Org1MSP", string(cert), string(key)) - err = wallet.Put("appUser", identity) - if err != nil { - return err - } - fmt.Println("============ populate wallet ends ============") - return nil + return wallet.Put("appUser", identity) } diff --git a/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore b/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore new file mode 100644 index 00000000..15958470 --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +coverage diff --git a/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js b/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js new file mode 100644 index 00000000..072edaf6 --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ +'use strict'; + +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'] + } +}; diff --git a/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js index c44990b2..83a62049 100644 --- a/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js +++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js @@ -67,335 +67,336 @@ 'use strict'; -const { Contract } = require('fabric-contract-api'); +const {Contract} = require('fabric-contract-api'); -class Chaincode extends Contract{ +class Chaincode extends Contract { - // CreateAsset - create a new asset, store into chaincode state - async CreateAsset(ctx, assetID, color, size, owner, appraisedValue) { - const exists = await this.AssetExists(ctx, assetID) - if (exists) { - throw new Error(`The asset ${assetID} already exists`) - } + // CreateAsset - create a new asset, store into chaincode state + async CreateAsset(ctx, assetID, color, size, owner, appraisedValue) { + const exists = await this.AssetExists(ctx, assetID); + if (exists) { + throw new Error(`The asset ${assetID} already exists`); + } - // ==== Create asset object and marshal to JSON ==== - let asset = {}; - asset.docType = 'asset'; - asset.ID = assetID; - asset.color = color; - asset.size = size; - asset.owner = owner; - asset.appraisedValue = appraisedValue; + // ==== Create asset object and marshal to JSON ==== + let asset = { + docType: 'asset', + assetID: assetID, + color: color, + size: size, + owner: owner, + appraisedValue: appraisedValue + }; - // === 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.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 - await ctx.stub.putState(colorNameIndexKey, Buffer.from('\u0000')); - } + // === 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]); - // ReadAsset returns the asset stored in the world state with given id. - async ReadAsset(ctx, id) { - const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state - if (!assetJSON || assetJSON.length === 0) { - throw new Error(`Asset ${id} does not exist`); - } + // 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 + await ctx.stub.putState(colorNameIndexKey, Buffer.from('\u0000')); + } - return assetJSON.toString(); - } + // ReadAsset returns the asset stored in the world state with given id. + async ReadAsset(ctx, id) { + const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state + if (!assetJSON || assetJSON.length === 0) { + throw new Error(`Asset ${id} does not exist`); + } - // delete - remove a asset key/value pair from state - async DeleteAsset(ctx, id) { - if (!id) { - throw new Error('Asset name must not be empty'); - } + return assetJSON.toString(); + } - var exists = await this.AssetExists(ctx, id) - if (!exists) { - throw new Error(`Asset ${id} does not exist`) - } + // delete - remove a asset key/value pair from state + async DeleteAsset(ctx, id) { + if (!id) { + throw new Error('Asset name must not be empty'); + } - // to maintain the color~name index, we need to read the asset first and get its color - let valAsbytes = await ctx.stub.getState(id); // get the asset from chaincode state - let jsonResp = {}; - if (!valAsbytes) { - jsonResp.error = 'Asset does not exist: ' + name; - throw new Error(jsonResp); - } - let assetJSON = {}; - try { - assetJSON = JSON.parse(valAsbytes.toString()); - } catch (err) { - jsonResp = {}; - jsonResp.error = 'Failed to decode JSON of: ' + id; - throw new Error(jsonResp); - } - await ctx.stub.deleteState(id); //remove the asset from chaincode state + let exists = await this.AssetExists(ctx, id); + if (!exists) { + throw new Error(`Asset ${id} does not exist`); + } - // delete the index - let indexName = 'color~name'; - let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.ID]); - if (!colorNameIndexKey) { - throw new Error(' Failed to create the createCompositeKey'); - } - // Delete index entry to state. - await ctx.stub.deleteState(colorNameIndexKey); - } + // to maintain the color~name index, we need to read the asset first and get its color + let valAsbytes = await ctx.stub.getState(id); // get the asset from chaincode state + let jsonResp = {}; + if (!valAsbytes) { + jsonResp.error = `Asset does not exist: ${id}`; + throw new Error(jsonResp); + } + let assetJSON; + try { + assetJSON = JSON.parse(valAsbytes.toString()); + } catch (err) { + jsonResp = {}; + jsonResp.error = `Failed to decode JSON of: ${id}`; + throw new Error(jsonResp); + } + await ctx.stub.deleteState(id); //remove the asset from chaincode state - // TransferAsset transfers an asset by setting a new owner name on the asset - async TransferAsset(ctx, assetName, newOwner) { + // delete the index + let indexName = 'color~name'; + let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.assetID]); + if (!colorNameIndexKey) { + throw new Error(' Failed to create the createCompositeKey'); + } + // Delete index entry to state. + await ctx.stub.deleteState(colorNameIndexKey); + } - let assetAsBytes = await ctx.stub.getState(assetName); - if (!assetAsBytes || !assetAsBytes.toString()) { - throw new Error(`Asset ${assetName} does not exist`); - } - let assetToTransfer = {}; - try { - assetToTransfer = JSON.parse(assetAsBytes.toString()); //unmarshal - } catch (err) { - let jsonResp = {}; - jsonResp.error = 'Failed to decode JSON of: ' + assetName; - throw new Error(jsonResp); - } - assetToTransfer.owner = newOwner; //change the owner + // TransferAsset transfers a asset by setting a new owner name on the asset + async TransferAsset(ctx, assetName, newOwner) { - let assetJSONasBytes = Buffer.from(JSON.stringify(assetToTransfer)); - await ctx.stub.putState(assetName, assetJSONasBytes); //rewrite the asset - } + let assetAsBytes = await ctx.stub.getState(assetName); + if (!assetAsBytes || !assetAsBytes.toString()) { + throw new Error(`Asset ${assetName} does not exist`); + } + let assetToTransfer = {}; + try { + assetToTransfer = JSON.parse(assetAsBytes.toString()); //unmarshal + } catch (err) { + let jsonResp = {}; + jsonResp.error = 'Failed to decode JSON of: ' + assetName; + throw new Error(jsonResp); + } + assetToTransfer.owner = newOwner; //change the owner -// GetAssetsByRange performs a range query based on the start and end keys provided. -// Read-only function results are not typically submitted to ordering. If the read-only -// results are submitted to ordering, or if the query is used in an update transaction -// and submitted to ordering, then the committing peers will re-execute to guarantee that -// result sets are stable between endorsement time and commit time. The transaction is -// invalidated by the committing peers if the result set has changed between endorsement -// time and commit time. -// Therefore, range queries are a safe option for performing update transactions based on query results. - async GetAssetsByRange(ctx, startKey, endKey) { + let assetJSONasBytes = Buffer.from(JSON.stringify(assetToTransfer)); + await ctx.stub.putState(assetName, assetJSONasBytes); //rewrite the asset + } - let resultsIterator = await ctx.stub.getStateByRange(startKey, endKey); - let results = await this.GetAllResults(resultsIterator, false); + // GetAssetsByRange performs a range query based on the start and end keys provided. + // Read-only function results are not typically submitted to ordering. If the read-only + // results are submitted to ordering, or if the query is used in an update transaction + // and submitted to ordering, then the committing peers will re-execute to guarantee that + // result sets are stable between endorsement time and commit time. The transaction is + // invalidated by the committing peers if the result set has changed between endorsement + // time and commit time. + // Therefore, range queries are a safe option for performing update transactions based on query results. + async GetAssetsByRange(ctx, startKey, endKey) { - return JSON.stringify(results); - } + let resultsIterator = await ctx.stub.getStateByRange(startKey, endKey); + let results = await this.GetAllResults(resultsIterator, false); - // TransferAssetBasedOnColor will transfer assets of a given color to a certain new owner. - // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'. - // Committing peers will re-execute range queries to guarantee that result sets are stable - // between endorsement time and commit time. The transaction is invalidated by the - // committing peers if the result set has changed between endorsement time and commit time. - // Therefore, range queries are a safe option for performing update transactions based on query results. - // Example: GetStateByPartialCompositeKey/RangeQuery - async TransferAssetByColor(ctx, color, newOwner) { - // Query the color~name index by color - // This will execute a key range query on all keys starting with 'color' - let coloredAssetResultsIterator = await ctx.stub.getStateByPartialCompositeKey('color~name', [color]); + return JSON.stringify(results); + } - // Iterate through result set and for each asset found, transfer to newOwner - while (true) { - let responseRange = await coloredAssetResultsIterator.next(); - if (!responseRange || !responseRange.value || !responseRange.value.key) { - return; - } + // TransferAssetBasedOnColor will transfer assets of a given color to a certain new owner. + // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'. + // Committing peers will re-execute range queries to guarantee that result sets are stable + // between endorsement time and commit time. The transaction is invalidated by the + // committing peers if the result set has changed between endorsement time and commit time. + // Therefore, range queries are a safe option for performing update transactions based on query results. + // Example: GetStateByPartialCompositeKey/RangeQuery + async TransferAssetByColor(ctx, color, newOwner) { + // Query the color~name index by color + // This will execute a key range query on all keys starting with 'color' + let coloredAssetResultsIterator = await ctx.stub.getStateByPartialCompositeKey('color~name', [color]); - let objectType; - let attributes; - ({ - objectType, - attributes - } = await ctx.stub.splitCompositeKey(responseRange.value.key)); + // Iterate through result set and for each asset found, transfer to newOwner + let responseRange = await coloredAssetResultsIterator.next(); + while (!responseRange.done) { + if (!responseRange || !responseRange.value || !responseRange.value.key) { + return; + } - console.log(objectType) - let returnedAssetName = attributes[1]; + let objectType; + let attributes; + ( + {objectType, attributes} = await ctx.stub.splitCompositeKey(responseRange.value.key) + ); - // Now call the transfer function for the found asset. - // Re-use the same function that is used to transfer individual assets - await this.TransferAsset(ctx, returnedAssetName, newOwner); - } - } + console.log(objectType); + let returnedAssetName = attributes[1]; - // QueryAssetsByOwner queries for assets based on a passed in owner. - // This is an example of a parameterized query where the query logic is baked into the chaincode, - // and accepting a single query parameter (owner). - // Only available on state databases that support rich query (e.g. CouchDB) - // Example: Parameterized rich query - async QueryAssetsByOwner(ctx, owner) { - let queryString = {}; - queryString.selector = {}; - queryString.selector.docType = 'asset'; - queryString.selector.owner = owner; - let queryResults = await this.GetQueryResultForQueryString(ctx, JSON.stringify(queryString)); - return queryResults; //shim.success(queryResults); - } + // Now call the transfer function for the found asset. + // Re-use the same function that is used to transfer individual assets + await this.TransferAsset(ctx, returnedAssetName, newOwner); + responseRange = await coloredAssetResultsIterator.next(); + } + } - // Example: Ad hoc rich query - // QueryAssets uses a query string to perform a query for assets. - // Query string matching state database syntax is passed in and executed as is. - // Supports ad hoc queries that can be defined at runtime by the client. - // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries. - // Only available on state databases that support rich query (e.g. CouchDB) - async QueryAssets(ctx, queryString) { - let queryResults = await this.GetQueryResultForQueryString(ctx, queryString); - return queryResults; - } + // QueryAssetsByOwner queries for assets based on a passed in owner. + // This is an example of a parameterized query where the query logic is baked into the chaincode, + // and accepting a single query parameter (owner). + // Only available on state databases that support rich query (e.g. CouchDB) + // Example: Parameterized rich query + async QueryAssetsByOwner(ctx, owner) { + let queryString = {}; + queryString.selector = {}; + queryString.selector.docType = 'asset'; + queryString.selector.owner = owner; + return await this.GetQueryResultForQueryString(ctx, JSON.stringify(queryString)); //shim.success(queryResults); + } - // 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) { + // Example: Ad hoc rich query + // QueryAssets uses a query string to perform a query for assets. + // Query string matching state database syntax is passed in and executed as is. + // Supports ad hoc queries that can be defined at runtime by the client. + // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries. + // Only available on state databases that support rich query (e.g. CouchDB) + async QueryAssets(ctx, queryString) { + return await this.GetQueryResultForQueryString(ctx, queryString); + } - let resultsIterator = await ctx.stub.getQueryResult(queryString); - let results = await this.GetAllResults(resultsIterator, false); + // 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) { - return JSON.stringify(results); - } + let resultsIterator = await ctx.stub.getQueryResult(queryString); + let results = await this.GetAllResults(resultsIterator, false); - // Example: Pagination with Range Query - // GetAssetsByRangeWithPagination performs a range query based on the start & end key, - // page size and a bookmark. - // 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) { + return JSON.stringify(results); + } - const { iterator, metadata } = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark); - const results = await this.GetAllResults(iterator, false); + // Example: Pagination with Range Query + // GetAssetsByRangeWithPagination performs a range query based on the start & end key, + // page size and a bookmark. + // 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) { - results.ResponseMetadata = { - RecordsCount: metadata.fetched_records_count, - Bookmark: metadata.bookmark, - }; - return JSON.stringify(results); - } + const {iterator, metadata} = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark); + const results = await this.GetAllResults(iterator, false); - // Example: Pagination with Ad hoc Rich Query - // QueryAssetsWithPagination uses a query string, page size and a bookmark to perform a query - // for assets. Query string matching state database syntax is passed in and executed as is. - // The number of fetched records would be equal to or lesser than the specified page size. - // Supports ad hoc queries that can be defined at runtime by the client. - // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries. - // Only available on state databases that support rich query (e.g. CouchDB) - // Paginated queries are only valid for read only transactions. - async QueryAssetsWithPagination(ctx, queryString, pageSize, bookmark) { + results.ResponseMetadata = { + RecordsCount: metadata.fetched_records_count, + Bookmark: metadata.bookmark, + }; + return JSON.stringify(results); + } - const { iterator, metadata } = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark); - const results = await this.GetAllResults(iterator, false); + // Example: Pagination with Ad hoc Rich Query + // QueryAssetsWithPagination uses a query string, page size and a bookmark to perform a query + // for assets. Query string matching state database syntax is passed in and executed as is. + // The number of fetched records would be equal to or lesser than the specified page size. + // Supports ad hoc queries that can be defined at runtime by the client. + // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries. + // Only available on state databases that support rich query (e.g. CouchDB) + // Paginated queries are only valid for read only transactions. + async QueryAssetsWithPagination(ctx, queryString, pageSize, bookmark) { - results.ResponseMetadata = { - RecordsCount: metadata.fetched_records_count, - Bookmark: metadata.bookmark, - }; + const {iterator, metadata} = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark); + const results = await this.GetAllResults(iterator, false); - return JSON.stringify(results); - } + results.ResponseMetadata = { + RecordsCount: metadata.fetched_records_count, + Bookmark: metadata.bookmark, + }; - // GetAssetHistory returns the chain of custody for an asset since issuance. - async GetAssetHistory(ctx, assetName) { + return JSON.stringify(results); + } - const resultsIterator = await ctx.stub.getHistoryForKey(assetName); - const results = await this.GetAllResults(resultsIterator, true); + // GetAssetHistory returns the chain of custody for an asset since issuance. + async GetAssetHistory(ctx, assetName) { - return JSON.stringify(results); - } + let resultsIterator = await ctx.stub.getHistoryForKey(assetName); + let results = await this.GetAllResults(resultsIterator, true); - // AssetExists returns true when asset with given ID exists in world state - async AssetExists(ctx, assetName) { - // ==== Check if asset already exists ==== - const assetState = await ctx.stub.getState(assetName); - if ( !assetState || assetState.length === 0 ) { - return false; - } - return true - } + return JSON.stringify(results); + } - async GetAllResults(iterator, isHistory) { - const allResults = []; - let res = await iterator.next(); - while (!res.done) { - const jsonRes = {}; + // 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); + return assetState && assetState.length > 0; + } - if (isHistory) { - jsonRes.TxId = res.value.txId; - jsonRes.Timestamp = res.value.timestamp; - jsonRes.IsDelete = res.value.isDelete; - } + async GetAllResults(iterator, isHistory) { + let allResults = []; + let res = await iterator.next(); + while (!res.done) { + 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); + } + res = await iterator.next(); + } + 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}; - } + // InitLedger creates sample assets in the ledger + async InitLedger(ctx) { + const assets = [ + { + assetID: 'asset1', + color: 'blue', + size: 5, + owner: 'Tom', + appraisedValue: 100 + }, + { + assetID: 'asset2', + color: 'red', + size: 5, + owner: 'Brad', + appraisedValue: 100 + }, + { + assetID: 'asset3', + color: 'green', + size: 10, + owner: 'Jin Soo', + appraisedValue: 200 + }, + { + assetID: 'asset4', + color: 'yellow', + size: 10, + owner: 'Max', + appraisedValue: 200 + }, + { + assetID: 'asset5', + color: 'black', + size: 15, + owner: 'Adriana', + appraisedValue: 250 + }, + { + assetID: 'asset6', + color: 'white', + size: 15, + owner: 'Michel', + appraisedValue: 250 + }, + ]; - 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 = [ - { - ID: 'asset1', - color: 'blue', - size: 5, - owner: 'Tom', - appraisedValue: 100 - }, - { - ID: 'asset2', - color: 'red', - size: 5, - owner: 'Brad', - appraisedValue: 100 - }, - { - ID: 'asset3', - color: 'green', - size: 10, - owner: 'Jin Soo', - appraisedValue: 200 - }, - { - ID: 'asset4', - color: 'yellow', - size: 10, - owner: 'Max', - appraisedValue: 200 - }, - { - ID: 'asset5', - color: 'black', - size: 15, - owner: 'Adriana', - appraisedValue: 250 - }, - { - ID: 'asset6', - color: 'white', - size: 15, - owner: 'Michel', - appraisedValue: 250 - }, - ]; - - for (let i = 0; i < assets.length; i++) { - await this.CreateAsset( - ctx, - assets[i].ID, - assets[i].color, - assets[i].size, - assets[i].owner, - assets[i].appraisedValue - ); - } - } + for (let asset in assets) { + await this.CreateAsset( + ctx, + asset.assetID, + asset.color, + asset.size, + asset.owner, + asset.appraisedValue + ); + } + } } module.exports = Chaincode; diff --git a/asset-transfer-sbe/chaincode-typescript/src/asset.ts b/asset-transfer-sbe/chaincode-typescript/src/asset.ts index 80c60638..58bd07a0 100644 --- a/asset-transfer-sbe/chaincode-typescript/src/asset.ts +++ b/asset-transfer-sbe/chaincode-typescript/src/asset.ts @@ -6,10 +6,9 @@ import { Object, Property } from 'fabric-contract-api'; @Object() export class Asset { - @Property() public ID: string; - + @Property() public Value: number; @@ -18,5 +17,4 @@ export class Asset { @Property() public OwnerOrg: string; - } diff --git a/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts b/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts index 149f6563..19921a11 100644 --- a/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts +++ b/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts @@ -17,7 +17,7 @@ export class AssetContract extends Contract { if (exists) { throw new Error(`The asset ${assetId} already exists`); } - const ownerOrg = this.getClientOrgId(ctx); + const ownerOrg = AssetContract.getClientOrgId(ctx); const asset = new Asset(); asset.ID = assetId; asset.Value = value; @@ -27,7 +27,7 @@ export class AssetContract extends Contract { // Create the asset await ctx.stub.putState(assetId, buffer); // Set the endorsement policy of the assetId Key, such that current owner Org Peer is required to endorse future updates - await this.setAssetStateBasedEndorsement(ctx, asset.ID, [ownerOrg]); + await AssetContract.setAssetStateBasedEndorsement(ctx, asset.ID, [ownerOrg]); } // ReadAsset returns asset with given assetId @@ -78,7 +78,7 @@ export class AssetContract extends Contract { // Update the asset await ctx.stub.putState(assetId, Buffer.from(JSON.stringify(asset))); // Re-Set the endorsement policy of the assetId Key, such that a new owner Org Peer is required to endorse future updates - await this.setAssetStateBasedEndorsement(ctx, asset.ID, [newOwnerOrg]); + await AssetContract.setAssetStateBasedEndorsement(ctx, asset.ID, [newOwnerOrg]); } // AssetExists returns true when asset with given ID exists @@ -89,14 +89,14 @@ export class AssetContract extends Contract { // setAssetStateBasedEndorsement sets an endorsement policy to the assetId Key // setAssetStateBasedEndorsement enforces that the owner Org Peers must endorse future update transactions for the specified assetId Key - private async setAssetStateBasedEndorsement(ctx: Context, assetId: string, ownerOrgs: string[]): Promise { - let ep = new KeyEndorsementPolicy(); - ep.addOrgs("MEMBER", ...ownerOrgs); + private static async setAssetStateBasedEndorsement(ctx: Context, assetId: string, ownerOrgs: string[]): Promise { + const ep = new KeyEndorsementPolicy(); + ep.addOrgs('MEMBER', ...ownerOrgs); await ctx.stub.setStateValidationParameter(assetId, ep.getPolicy()); } // getClientOrgId gets the client's OrgId (MSPID) - private getClientOrgId(ctx: Context): string { + private static getClientOrgId(ctx: Context): string { return ctx.clientIdentity.getMSPID(); } } diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index bd9c3d9c..f70cf107 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -76,6 +76,14 @@ jobs: vmImage: ubuntu-18.04 strategy: matrix: + Basic-Application-Go: + DIRECTORY: asset-transfer-basic + LANGUAGE: go + TYPE: application + Basic-Application-Java: + DIRECTORY: asset-transfer-basic + LANGUAGE: java + TYPE: application Basic-Application-Javascript: DIRECTORY: asset-transfer-basic LANGUAGE: javascript @@ -96,18 +104,30 @@ jobs: DIRECTORY: asset-transfer-basic LANGUAGE: typescript TYPE: chaincode + Ledger-Application-Java: + DIRECTORY: asset-transfer-ledger-queries + LANGUAGE: java + TYPE: application Ledger-Chaincode-Go: DIRECTORY: asset-transfer-ledger-queries LANGUAGE: go - TYPE: chaincode - PrivateData-Chaincode-Go: - DIRECTORY: asset-transfer-private-data - LANGUAGE: go + TYPE: chaincode + Ledger-Chaincode-Javascript: + DIRECTORY: asset-transfer-ledger-queries + LANGUAGE: javascript TYPE: chaincode PrivateData-Application-Javascript: DIRECTORY: asset-transfer-private-data LANGUAGE: javascript - TYPE: application + TYPE: application + PrivateData-Chaincode-Go: + DIRECTORY: asset-transfer-private-data + LANGUAGE: go + TYPE: chaincode + SBE-Chaincode-Typescript: + DIRECTORY: asset-transfer-sbe + LANGUAGE: typescript + TYPE: chaincode Secured-Chaincode-Go: DIRECTORY: asset-transfer-secured-agreement LANGUAGE: go @@ -158,6 +178,9 @@ jobs: Ledger-Go: CHAINCODE_NAME: ledger CHAINCODE_LANGUAGE: go + Ledger-Javascript: + CHAINCODE_NAME: ledger + CHAINCODE_LANGUAGE: javascript steps: - template: templates/install-deps.yml - script: ../ci/scripts/run-test-network-ledger.sh @@ -179,6 +202,21 @@ jobs: workingDirectory: test-network displayName: Run Test Network Private Chaincode + - job: TestNetworkSBE + displayName: Test Network + pool: + vmImage: ubuntu-18.04 + strategy: + matrix: + SBE-Typescript: + CHAINCODE_NAME: sbe + CHAINCODE_LANGUAGE: typescript + steps: + - template: templates/install-deps.yml + - script: ../ci/scripts/run-test-network-sbe.sh + workingDirectory: test-network + displayName: Run Test Network Private Chaincode + - job: TestNetworkSecured displayName: Test Network pool: diff --git a/ci/scripts/run-test-network-basic.sh b/ci/scripts/run-test-network-basic.sh index 903e634a..58cf8168 100755 --- a/ci/scripts/run-test-network-basic.sh +++ b/ci/scripts/run-test-network-basic.sh @@ -11,20 +11,42 @@ function print() { echo -e "${GREEN}${1}${NC}" } -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}" +function createNetwork() { + 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}" +} +function stopNetwork() { + print "Stopping network" + ./network.sh down +} + +# Run Go application +#createNetwork +#print "Initializing Go application" +#pushd ../asset-transfer-basic/application-go +#print "Executing AssetTransfer.go" +#go run . +#popd +#stopNetwork + +# Run Java application +createNetwork +print "Initializing Java application" +pushd ../asset-transfer-basic/application-java +print "Executing Gradle Run" +gradle run +popd +stopNetwork # Run Javascript application +createNetwork print "Initializing Javascript application" pushd ../asset-transfer-basic/application-javascript npm install print "Executing app.js" node app.js popd - -print "Stopping network" -./network.sh down -rm -R ../asset-transfer-basic/application-javascript/wallet +stopNetwork diff --git a/ci/scripts/run-test-network-ledger.sh b/ci/scripts/run-test-network-ledger.sh index 36b96b91..bbfed65a 100755 --- a/ci/scripts/run-test-network-ledger.sh +++ b/ci/scripts/run-test-network-ledger.sh @@ -1,6 +1,7 @@ set -euo pipefail FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} CHAINCODE_NAME=${CHAINCODE_NAME:-ledger} function print() { @@ -10,31 +11,33 @@ function print() { echo -e "${GREEN}${1}${NC}" } -print "Creating network" -./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}" +function createNetwork() { + 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 +function stopNetwork() { + print "Stopping network" + ./network.sh down +} -# Run Javascript application against the go chaincode +# Run Java application +createNetwork +print "Initializing Java application" +pushd ../asset-transfer-ledger-queries/application-java +print "Executing Gradle Run" +gradle run +popd +stopNetwork + +# Run Javascript application +createNetwork 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 \ No newline at end of file +stopNetwork diff --git a/ci/scripts/run-test-network-private.sh b/ci/scripts/run-test-network-private.sh index c38cf978..8f867fde 100755 --- a/ci/scripts/run-test-network-private.sh +++ b/ci/scripts/run-test-network-private.sh @@ -11,19 +11,24 @@ function print() { echo -e "${GREEN}${1}${NC}" } -print "Creating network" -./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}" +function createNetwork() { + 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 private-data ${CHAINCODE_NAME} chaincode" -./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json +function stopNetwork() { + print "Stopping network" + ./network.sh down +} # Run Javascript application -print "Initializing Javascript application" -pushd ../asset-transfer-private-data/application-javascript -npm install -print "Executing app.js" -node app.js -popd - -print "Stopping network" -./network.sh down +createNetwork +#print "Initializing Javascript application" +#pushd ../asset-transfer-private-data/application-javascript +#npm install +#print "Executing app.js" +#node app.js +#popd +stopNetwork diff --git a/ci/scripts/run-test-network-sbe.sh b/ci/scripts/run-test-network-sbe.sh new file mode 100755 index 00000000..35eb0d59 --- /dev/null +++ b/ci/scripts/run-test-network-sbe.sh @@ -0,0 +1,28 @@ +set -euo pipefail + +FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-typescript} +CHAINCODE_NAME=${CHAINCODE_NAME:-sbe} + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + echo -e "${GREEN}${1}${NC}" +} + +function createNetwork() { + 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}" +} + +function stopNetwork() { + print "Stopping network" + ./network.sh down +} + +# Run Javascript application +createNetwork +stopNetwork diff --git a/ci/scripts/run-test-network-secured.sh b/ci/scripts/run-test-network-secured.sh index b5bdc029..97b5ac25 100755 --- a/ci/scripts/run-test-network-secured.sh +++ b/ci/scripts/run-test-network-secured.sh @@ -11,11 +11,18 @@ function print() { echo -e "${GREEN}${1}${NC}" } -print "Creating network" -./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}" +function createNetwork() { + 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} chaincode" -./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" +function stopNetwork() { + print "Stopping network" + ./network.sh down +} -print "Stopping network" -./network.sh down +# Run Javascript application +createNetwork +stopNetwork From 996344938756611ad6658ae99a30f2b13bc7ca25 Mon Sep 17 00:00:00 2001 From: Chris Gabriel Date: Sat, 15 Aug 2020 07:22:20 -0600 Subject: [PATCH 2/9] Update README table to reflect addition of Go application Signed-off-by: Chris Gabriel --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f8300cb..5168d4cb 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ transfer an asset in a more realistic transfer scenario. | **Smart Contract** | **Description** | **Tutorial** | **Smart contract languages** | **Application languages** | | -----------|------------------------------|----------|---------|---------| -| [Basic](asset-transfer-basic) | The Basic sample smart contract that allows you to create and transfer an asset by putting data on the ledger and retrieving it. This sample is recommended for new Fabric users. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, JavaScript, TypeScript, Java | JavaScript, Java | +| [Basic](asset-transfer-basic) | The Basic sample smart contract that allows you to create and transfer an asset by putting data on the ledger and retrieving it. This sample is recommended for new Fabric users. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, JavaScript, TypeScript, Java | Go, JavaScript, Java | | [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates range queries and transaction updates using range queries (applicable for both LevelDB and CouchDB state databases), and how to deploy an index with your chaincode to support JSON queries (applicable for CouchDB state database only). | [Using CouchDB](https://hyperledger-fabric.readthedocs.io/en/master/couchdb_tutorial.html) | Go, JavaScript | Java | | [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections, how to manage private data collections with the chaincode lifecycle, and how the private data hash can be used to verify private data on the ledger. It also demonstrates how to control asset updates and transfers using client-based ownership and access control. | [Using Private Data](https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-private-data/chaincode-go) | Go | JavaScript | | [State-Based Endorsement](asset-transfer-sbe) | This sample demonstrates how to override the chaincode-level endorsement policy to set endorsement policies at the key-level (data/asset level). | [Using State-based endorsement](https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-sbe) | TypeScript | **Coming soon** | From fdebe1f2e67bbb0a59e29449e657e87d886acc4f Mon Sep 17 00:00:00 2001 From: nikhil550 Date: Wed, 19 Aug 2020 12:50:33 -0400 Subject: [PATCH 3/9] Update sbe transfer sample readme (#304) Signed-off-by: NIKHIL E GUPTA Co-authored-by: NIKHIL E GUPTA --- asset-transfer-sbe/README.md | 142 +++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 31 deletions(-) diff --git a/asset-transfer-sbe/README.md b/asset-transfer-sbe/README.md index 60a7cf4d..c917812d 100644 --- a/asset-transfer-sbe/README.md +++ b/asset-transfer-sbe/README.md @@ -1,55 +1,135 @@ -# Sample to demonstrate State-Based Endorsements (SBE) +# State-based endorsement asset transfer sample -## Introduction to SBE -Fabric allows different ways to set endorsements for transactions on the network. The default method is specified in chaincode definition, which is agreed by channel members and committed to the channel. However, there are cases where it may be necessary for a particular Key (public channel state or private data collection state) to have a different endorsement policy. State-Based endorsement allows endorsement policies to be overridden for the specified Key's. State-Based Endorsements are also known as Key Level Endorsements. To learn more about endorsement policies and State-Based endorsements, visit the [Fabric Endorsement Policies documentation](https://hyperledger-fabric.readthedocs.io/en/master/endorsement-policies.html). +Transactions that are submitted to Hyperledger Fabric networks need to be endorsed by peers that are joined to a channel before the transaction can be added to the ledger. Fabric peers endorse transactions by executing a smart contract using the inputs of the transaction proposal. The peers then sign the input and output generated by the smart contract execution. The endorsement policy specifies the set of organizations whose peers need to endorse a transaction before it can be added to the ledger. + +Each chaincode that is deployed to a channel has an endorsement policy that governs the assets managed by the chaincode smart contracts. However, you can override the chaincode level endorsement policy to create an endorsement policy for a specific key, either on the public channel ledger or in a private collection. State-based endorsement policies, also known as key-level endorsement policies, allow channel members use different endorsement policies for assets that are managed by the same smart contract. For more information about endorsement policies and state-based endorsement, visit the [Endorsement Policies](https://hyperledger-fabric.readthedocs.io/en/master/endorsement-policies.html) topic in the Fabric documentation. + +## About the Sample + +The state-based endorsement (SBE) asset transfer sample demonstrates how to use key-level endorsement policies to ensure that an asset only needs to be endorsed by an asset owner. In the course of the tutorial, you will use the smart contract to complete the following transfer scenario: + +- Deploy the SBE smart contract to a channel that was created using the Fabric test network. The channel will have two members, Org1 and Org2, that will participate in the asset transfer. Each organization operates one peer that is joined to the channel. +- Create an asset using the chaincode endorsement policy. The chaincode level endorsement policy requires that a majority of organizations on the channel to endorse a transaction. As a result, the transaction that creates the asset needs to be endorsed by peers that belong to Org1 and Org2. When the asset is created, the smart contract creates a state-based endorsement policy that specifies that only the organization that owns that asset needs to endorse any asset updates. Because the asset is owned by Org1, any future updates to the asset need to be endorsed by the Org1 peer. +- Update the asset using the state-based endorsement policy. +- Transfer the asset to Org2. The transfer transaction will create a new state-based endorsement policy that reflects the new asset owner. +- Update the asset once more, this time with Org2 as the owner. Because the state-based endorsement policy has been updated, this transaction only needs to be endorsed by Org2. -Fabric Shim Method's for State-Based endorsements (below references from fabric-shim, ChaincodeStub for Node): -- setStateValidationParameter(key: string, ep: Uint8Array): Promise; -- getStateValidationParameter(key: string): Promise; -- setPrivateDataValidationParameter(collection: string, key: string, ep: Uint8Array): Promise; -- getPrivateDataValidationParameter(collection: string, key: string): Promise; +## Deploy the smart contract +We are going to run the SBE smart contract using the Fabric test network. Open a command terminal and navigate to test network directory in your local clone of the `fabric-samples`. We will operate from the `test-network` directory for the remainder of the tutorial. +``` +cd fabric-samples/test-network +``` -## About the SBE Asset Transfer Sample -Using this sample we demonstrate State-Based Endorsements (SBE), and set a different endorsement policy for a specified Key, instead of the default chaincode policy. This sample is deployed and demonstrated on the fabric-samples, Test network (2 Org network, Org1 and Org2). The endorsement policy for chaincodes deployed in this network defaults to majority, that is both Org's need to endorse transactions on the network. To bootstrap the Test Network, and deploy the SBE Asset Transfer Sample chaincode, visit the [Test Network documentation](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html). +Run the following command to deploy the test network and create a channel named `mychannel`: -This sample demonstrates modifying the default chaincode policy (requiring endorsements from both Org Peers), and changing it to a less restrictive policy requiring endorsement only from a specific Org Peer for the specified Key. Endorsements can also be modified to be more restrictive for a particular Key in a similar way. +``` +./network.sh up createChannel +``` -On Asset creation, State-Based endorsements for the Asset Key is set to either Org1 or Org2 Peer, depending on the client Org creating the Asset. Creation of Asset still needs the default chaincode endorsement policy, and hence will need to be endorsed by both Org's (Org1 & Org2). However, all future updates for the Asset Key will use the modified endorsements as per the State-Based endorsements set during Asset creation. +You can use the test network script to deploy the smart contract to the channel that was just created. The script uses the Fabric chaincode lifecycle to deploy the smart contract to the channel. We will use the default chaincode level endorsement policy used by the Fabric chaincode lifecycle, which requires an endorsement from a majority of channel members. In our case, this will require that both Org1 and Org2 endorse a transaction (2 of 2). Deploy the smart contract to `mychannel` using the following command: +``` +./network.sh deployCC -ccn sbe -ccl typescript +``` -Hence transactions like UpdateAsset or TransferAsset will not succeed if endorsed by the non-owner Org Peer. +Set the following environment variables to interact with the network as a user from Org1: -Sample invokations to demonstrate State-Based Endorsements (SBE) on test-network, using client identity of Org1 -* Create Asset (requires Org1 & Org2 Peer to endorse as per the default chaincode endorsement policy, however future updates will need only Org1 Peer to endorse, as the State-Based endorsement policy is set for assetId Key during the createAsset transaction) -```bash +``` +export PATH=${PWD}/../bin:${PWD}:$PATH +export FABRIC_CFG_PATH=$PWD/../config/ +export CORE_PEER_TLS_ENABLED=true +export CORE_PEER_LOCALMSPID="Org1MSP" +export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp +export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt +export CORE_PEER_ADDRESS=localhost:7051 +``` + +## Run the transfer scenario + +We can now invoke the SBE smart contract to create a new asset: +``` peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"CreateAsset","Args":["asset1","100","Org1User1"]}' ``` -* Read & Verify Asset (Use this to verify changes for UpdateAsset & TransferAsset) -```bash +The create transaction needs to target both peers from Org1 and Org2 to meet the chaincode endorsement policy. The chaincode will read the MSP ID of the client user submitting the transaction and assign that organization as the asset owner. As a result, the asset will initially be owned by Org1. + +You can query the asset using with the following command: +``` peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}' ``` -* Update Asset (requires Org1 Peer to endorse as per SBE specified for assetId Key, but since Org2 Peer is endorsing it will result in ENDORSEMENT_POLICY_FAILURE by the peer during endorsement validations for the transaction) -```bash +The result is a new asset owned by Org1, identified using the Org1 MSP ID `Org1MSP`: +`{"ID":"asset1","Value":100,"Owner":"Org1User1","OwnerOrg":"Org1MSP"}` + +In addition to creating the asset, the `CreateAsset` function also sets a state-based endorsement policy for the asset. Only a peer of the asset owner, can successfully endorse an asset update. To demonstrate the key-level endorsement policy, lets try to update the asset while targeting the Org2 peer: +``` peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","200"]}' ``` -* Update Asset succeeds (requires Org1 Peer to endorse as per SBE specified for assetId Key, and Org1 Peer is endorsing here) -```bash +The result is an endorsement policy failure: +``` +Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE) - proposal response: +``` + +If we attempt to update the asset with an endorsement from the Org1 peer, the update succeeds: +``` peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","200"]}' ``` -* Transfer Asset (requires Org1 Peer to endorse as per SBE specified for assetId Key, but since Org2 Peer is endorsing it will result in ENDORSEMENT_POLICY_FAILURE by the peer during endorsement validations for the transaction) -```bash -peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset1","Org2User1","Org2MSP"]}' +You can query the asset one more time to verify that the update was successful: +``` +peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}' +``` + +The asset value is now 200: +``` +{"ID":"asset1","Value":200,"Owner":"Org1User1","OwnerOrg":"Org1MSP"} +``` + +Now that we have tested the asset key-level endorsement policy, we can transfer the asset to Org2. Run the following command to transfer the asset from Org1 to Org2. This time the Org2 MSP ID is provided as a transaction input. The `TransferAsset` function will update the endorsement policy to specify that only a peer of the new owner can update the asset. Note that this command targets the Org1 peer. + ``` -* Transfer Asset succeeds (requires Org1 Peer to endorse as per SBE specified for assetId Key, and Org1 Peer is endorsing here) -```bash peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset1","Org2User1","Org2MSP"]}' ``` -* Update Asset (now requires Org2 Peer to endorse as per SBE specified for assetId Key, but since Org1 Peer is endorsing it will result in ENDORSEMENT_POLICY_FAILURE by the peer during endorsement validations for the transaction) -```bash + +We can query the asset to see that the owner has been updated from Org1 to Org2: +``` +peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}' +``` + +The owning organization is now Org2: +``` +{"ID":"asset1","Value":200,"Owner":"Org2User1","OwnerOrg":"Org2MSP"} +``` + +Org2 now needs to endorse any asset updates. Run the following command to try to update the asset with an endorsement from the Org1 peer: +``` peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","300"]}' ``` -* Update Asset succeeds (requires Org2 Peer to endorse as per SBE specified for assetId Key, and Org2 Peer is endorsing here) -```bash + +The response will be an endorsement policy failure: +``` +Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE) - proposal response: +``` + +Now try to update the asset with an endorsement from the Org2 peer: +``` peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","300"]}' ``` + +You can query the asset again to verify that the transaction update succeeded: +``` +peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}' +``` + +The asset value is now 300: +``` +{"ID":"asset1","Value":300,"Owner":"Org2User1","OwnerOrg":"Org2MSP"} +``` + +Note that the transaction to update the asset was submitted by a user from Org1, even though the asset was owned by Org2. The transfer enabled by the SBE smart contract is a simple scenario meant only to demonstrate the use of state-based endorsement policies. The smart contract can use access control to specify that an asset can only be updated by its owner. Private data collections can also be used to ensure that transfers need to be endorsed by the owner and recipient of the transfer, instead of just the asset owner. For a more realistic example of an asset transfer scenario, see the [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_asset_transfer/secured_private_asset_transfer_tutorial.html) tutorial. + +## Clean up + +When you are finished, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created: + +``` +./network.sh down +``` From 5173e73e7b2395c174c50aa89051ac838777d11e Mon Sep 17 00:00:00 2001 From: Arnaud J Le Hors Date: Wed, 19 Aug 2020 22:03:12 +0200 Subject: [PATCH 4/9] Improve basic asset transfer external chaincode detect script This change makes the way the type property is extracted from the metadata.json file much more robust, and therefore sets a better example. Signed-off-by: Arnaud J Le Hors --- .../chaincode-external/sampleBuilder/bin/detect | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect index 9fea8f35..d4b70078 100755 --- a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect +++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect @@ -4,8 +4,10 @@ set -euo pipefail METADIR=$2 # check if the "type" field is set to "external" -# crude way without jq, fragile but good enough for this sample... -if [ "$(grep type "$METADIR/metadata.json" |cut -f4 -d\")" = "external" ]; then +# crude way without jq which is not in the default fabric peer image +TYPE=$(tr -d '\n' < "$METADIR/metadata.json" | awk -F':' '{ for (i = 1; i < NF; i++){ if ($i~/type/) { print $(i+1); break }}}'| cut -d\" -f2) + +if [ "$TYPE" = "external" ]; then exit 0 fi From 8bb870f9303e5e73c532307378f8baa663e27d73 Mon Sep 17 00:00:00 2001 From: Dereck Date: Thu, 20 Aug 2020 15:40:18 -0400 Subject: [PATCH 5/9] Fix minor issues related to asset-transfer-basic application-go (#291) * added additional logs to application. * minor fixes Signed-off-by: Chongxin Luo --- .../application-go/assetTransfer.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/asset-transfer-basic/application-go/assetTransfer.go b/asset-transfer-basic/application-go/assetTransfer.go index a8873281..0dfbb877 100644 --- a/asset-transfer-basic/application-go/assetTransfer.go +++ b/asset-transfer-basic/application-go/assetTransfer.go @@ -81,17 +81,34 @@ func main() { } log.Println(string(result)) - result, err = contract.EvaluateTransaction("ReadAsset", "asset4") + log.Println("--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments") + result, err = contract.SubmitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300") if err != nil { log.Fatalf("Failed to evaluate transaction: %v", err) } log.Println(string(result)) + log.Println("--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID") + result, err = contract.EvaluateTransaction("ReadAsset", "asset13") + if err != nil { + log.Fatalf("failed to evaluate transaction: %v\n", err) + } + log.Println(string(result)) + + log.Println("--> Evaluate Transaction: AssetExists, function returns 'true' if an asset with given assetID exist") + result, err = contract.EvaluateTransaction("AssetExists", "asset1") + if err != nil { + log.Fatalf("failed to evaluate transaction: %v\n", err) + } + log.Println(string(result)) + + log.Println("--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom") _, err = contract.SubmitTransaction("TransferAsset", "asset1", "Tom") if err != nil { log.Fatalf("Failed to submit transaction: %v", err) } + log.Println("--> Evaluate Transaction: ReadAsset, function returns 'asset1' attributes") result, err = contract.EvaluateTransaction("ReadAsset", "asset1") if err != nil { log.Fatalf("Failed to evaluate transaction: %v", err) From b3bc39ae5fbc3b5003295fd00852d518962ae108 Mon Sep 17 00:00:00 2001 From: David Enyeart Date: Mon, 17 Aug 2020 17:42:42 -0400 Subject: [PATCH 6/9] Re-enable CI tests for asset-transfer-private-data JS app Re-enable CI tests for asset-transfer-private-data JavaScript application. Also re-add the error when private asset details are not found in the collection, so that the CLI instructions work as desired, and to demonstrate error handing in the client application. Signed-off-by: David Enyeart --- .../application-javascript/app.js | 4 ++-- .../chaincode-go/private_asset_queries.go | 1 - ci/scripts/run-test-network-private.sh | 14 +++++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/asset-transfer-private-data/application-javascript/app.js b/asset-transfer-private-data/application-javascript/app.js index edac6185..a51798fb 100644 --- a/asset-transfer-private-data/application-javascript/app.js +++ b/asset-transfer-private-data/application-javascript/app.js @@ -123,7 +123,7 @@ async function main() { let asset2Data = { objectType: assetType, assetID: assetID2, color: 'blue', size: 35, appraisedValue: 727 }; console.log('\n**************** As Org1 Client ****************'); - console.log('Adding Assets to work with: Submit Transaction: CreateAsset ' + assetID1); + console.log('Adding Assets to work with:\n--> Submit Transaction: CreateAsset ' + assetID1); let statefulTxn = contractOrg1.createTransaction('CreateAsset'); //if you need to customize endorsement to specific set of Orgs, use setEndorsingOrganizations //statefulTxn.setEndorsingOrganizations(mspOrg1); @@ -255,4 +255,4 @@ async function main() { } } -main(); \ No newline at end of file +main(); diff --git a/asset-transfer-private-data/chaincode-go/private_asset_queries.go b/asset-transfer-private-data/chaincode-go/private_asset_queries.go index ea834a82..287590f1 100644 --- a/asset-transfer-private-data/chaincode-go/private_asset_queries.go +++ b/asset-transfer-private-data/chaincode-go/private_asset_queries.go @@ -147,7 +147,6 @@ func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInte return queryResults, nil } - // QueryAssets uses a query string to perform a query for assets. // Query string matching state database syntax is passed in and executed as is. // Supports ad hoc queries that can be defined at runtime by the client. diff --git a/ci/scripts/run-test-network-private.sh b/ci/scripts/run-test-network-private.sh index 8f867fde..b5b35884 100755 --- a/ci/scripts/run-test-network-private.sh +++ b/ci/scripts/run-test-network-private.sh @@ -15,7 +15,7 @@ function createNetwork() { 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}" + ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json } function stopNetwork() { @@ -25,10 +25,10 @@ function stopNetwork() { # Run Javascript application createNetwork -#print "Initializing Javascript application" -#pushd ../asset-transfer-private-data/application-javascript -#npm install -#print "Executing app.js" -#node app.js -#popd +print "Initializing Javascript application" +pushd ../asset-transfer-private-data/application-javascript +npm install +print "Executing app.js" +node app.js +popd stopNetwork From be3ce853dc6366deaac278323ce8429bfdde713f Mon Sep 17 00:00:00 2001 From: Naser Mirzaei Date: Fri, 21 Aug 2020 18:20:21 +0430 Subject: [PATCH 7/9] hide set +x from output Signed-off-by: Naser Mirzaei --- .../scripts/check-commit-readiness.sh | 2 +- .../network/scripts/check-commit-readiness.sh | 2 +- test-network/addOrg3/addOrg3.sh | 4 +- .../addOrg3/fabric-ca/registerEnroll.sh | 16 +++---- test-network/network.sh | 8 ++-- .../organizations/fabric-ca/registerEnroll.sh | 44 +++++++++---------- test-network/scripts/createChannel.sh | 10 ++--- test-network/scripts/deployCC.sh | 18 ++++---- .../scripts/org3-scripts/step1org3.sh | 12 ++--- .../scripts/org3-scripts/step2org3.sh | 4 +- 10 files changed, 60 insertions(+), 60 deletions(-) diff --git a/high-throughput/scripts/check-commit-readiness.sh b/high-throughput/scripts/check-commit-readiness.sh index bf180920..7d4b9c39 100755 --- a/high-throughput/scripts/check-commit-readiness.sh +++ b/high-throughput/scripts/check-commit-readiness.sh @@ -43,7 +43,7 @@ checkCommitReadiness() { set -x peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name bigdatacc --signature-policy "OR('Org1MSP.peer', 'Org2MSP.peer')" --version 0 --init-required --sequence 1 >&log.txt res=$? - set +x + { set +x; } 2>/dev/null test $res -eq 0 || continue let rc=0 for var in "$@" diff --git a/interest_rate_swaps/network/scripts/check-commit-readiness.sh b/interest_rate_swaps/network/scripts/check-commit-readiness.sh index 55300d84..f56bf1a2 100644 --- a/interest_rate_swaps/network/scripts/check-commit-readiness.sh +++ b/interest_rate_swaps/network/scripts/check-commit-readiness.sh @@ -17,7 +17,7 @@ checkCommitReadiness() { set -x peer lifecycle chaincode checkcommitreadiness -o irs-orderer:7050 --channelID irs --signature-policy "AND(OR('partya.peer','partyb.peer','partyc.peer'), 'auditor.peer')" --name irscc --version 1 --init-required --sequence 1 >&log.txt res=$? - set +x + { set +x; } 2>/dev/null test $res -eq 0 || continue let rc=0 for var in "$@" diff --git a/test-network/addOrg3/addOrg3.sh b/test-network/addOrg3/addOrg3.sh index 47fb947f..4d50a193 100755 --- a/test-network/addOrg3/addOrg3.sh +++ b/test-network/addOrg3/addOrg3.sh @@ -73,7 +73,7 @@ function generateOrg3() { set -x cryptogen generate --config=org3-crypto.yaml --output="../organizations" res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo "Failed to generate certificates..." exit 1 @@ -131,7 +131,7 @@ function generateOrg3Definition() { set -x configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo "Failed to generate Org3 config material..." exit 1 diff --git a/test-network/addOrg3/fabric-ca/registerEnroll.sh b/test-network/addOrg3/fabric-ca/registerEnroll.sh index 86cc1209..a0dc729e 100644 --- a/test-network/addOrg3/fabric-ca/registerEnroll.sh +++ b/test-network/addOrg3/fabric-ca/registerEnroll.sh @@ -13,7 +13,7 @@ function createOrg3 { set -x fabric-ca-client enroll -u https://admin:adminpw@localhost:11054 --caname ca-org3 --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo 'NodeOUs: Enable: true @@ -35,21 +35,21 @@ function createOrg3 { echo set -x fabric-ca-client register --caname ca-org3 --id.name peer0 --id.secret peer0pw --id.type peer --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register user" echo set -x fabric-ca-client register --caname ca-org3 --id.name user1 --id.secret user1pw --id.type client --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register the org admin" echo set -x fabric-ca-client register --caname ca-org3 --id.name org3admin --id.secret org3adminpw --id.type admin --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null mkdir -p ../organizations/peerOrganizations/org3.example.com/peers mkdir -p ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com @@ -59,7 +59,7 @@ function createOrg3 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:11054 --caname ca-org3 -M ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp --csr.hosts peer0.org3.example.com --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp/config.yaml @@ -68,7 +68,7 @@ function createOrg3 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:11054 --caname ca-org3 -M ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls --enrollment.profile tls --csr.hosts peer0.org3.example.com --csr.hosts localhost --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/tlscacerts/* ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt @@ -92,7 +92,7 @@ function createOrg3 { echo set -x fabric-ca-client enroll -u https://user1:user1pw@localhost:11054 --caname ca-org3 -M ${PWD}/../organizations/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/config.yaml @@ -103,7 +103,7 @@ function createOrg3 { echo set -x fabric-ca-client enroll -u https://org3admin:org3adminpw@localhost:11054 --caname ca-org3 -M ${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/config.yaml diff --git a/test-network/network.sh b/test-network/network.sh index 95ac5147..de8126d6 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -214,7 +214,7 @@ function createOrgs() { set -x cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 @@ -227,7 +227,7 @@ function createOrgs() { set -x cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 @@ -240,7 +240,7 @@ function createOrgs() { set -x cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations" res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 @@ -329,7 +329,7 @@ function createConsortium() { set -x configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate orderer genesis block..."$'\e[0m' exit 1 diff --git a/test-network/organizations/fabric-ca/registerEnroll.sh b/test-network/organizations/fabric-ca/registerEnroll.sh index b5bf0828..9ba76b50 100755 --- a/test-network/organizations/fabric-ca/registerEnroll.sh +++ b/test-network/organizations/fabric-ca/registerEnroll.sh @@ -13,7 +13,7 @@ function createOrg1 { set -x fabric-ca-client enroll -u https://admin:adminpw@localhost:7054 --caname ca-org1 --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo 'NodeOUs: Enable: true @@ -35,21 +35,21 @@ function createOrg1 { echo set -x fabric-ca-client register --caname ca-org1 --id.name peer0 --id.secret peer0pw --id.type peer --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register user" echo set -x fabric-ca-client register --caname ca-org1 --id.name user1 --id.secret user1pw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register the org admin" echo set -x fabric-ca-client register --caname ca-org1 --id.name org1admin --id.secret org1adminpw --id.type admin --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null mkdir -p organizations/peerOrganizations/org1.example.com/peers mkdir -p organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com @@ -59,7 +59,7 @@ function createOrg1 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp --csr.hosts peer0.org1.example.com --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/config.yaml @@ -68,7 +68,7 @@ function createOrg1 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls --enrollment.profile tls --csr.hosts peer0.org1.example.com --csr.hosts localhost --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt @@ -92,7 +92,7 @@ function createOrg1 { echo set -x fabric-ca-client enroll -u https://user1:user1pw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/config.yaml @@ -103,7 +103,7 @@ function createOrg1 { echo set -x fabric-ca-client enroll -u https://org1admin:org1adminpw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/config.yaml @@ -123,7 +123,7 @@ function createOrg2 { set -x fabric-ca-client enroll -u https://admin:adminpw@localhost:8054 --caname ca-org2 --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo 'NodeOUs: Enable: true @@ -145,21 +145,21 @@ function createOrg2 { echo set -x fabric-ca-client register --caname ca-org2 --id.name peer0 --id.secret peer0pw --id.type peer --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register user" echo set -x fabric-ca-client register --caname ca-org2 --id.name user1 --id.secret user1pw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register the org admin" echo set -x fabric-ca-client register --caname ca-org2 --id.name org2admin --id.secret org2adminpw --id.type admin --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null mkdir -p organizations/peerOrganizations/org2.example.com/peers mkdir -p organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com @@ -169,7 +169,7 @@ function createOrg2 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp --csr.hosts peer0.org2.example.com --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/config.yaml @@ -178,7 +178,7 @@ function createOrg2 { echo set -x fabric-ca-client enroll -u https://peer0:peer0pw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls --enrollment.profile tls --csr.hosts peer0.org2.example.com --csr.hosts localhost --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt @@ -202,7 +202,7 @@ function createOrg2 { echo set -x fabric-ca-client enroll -u https://user1:user1pw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/config.yaml @@ -213,7 +213,7 @@ function createOrg2 { echo set -x fabric-ca-client enroll -u https://org2admin:org2adminpw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/config.yaml @@ -232,7 +232,7 @@ function createOrderer { set -x fabric-ca-client enroll -u https://admin:adminpw@localhost:9054 --caname ca-orderer --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo 'NodeOUs: Enable: true @@ -255,14 +255,14 @@ function createOrderer { echo set -x fabric-ca-client register --caname ca-orderer --id.name orderer --id.secret ordererpw --id.type orderer --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null echo echo "Register the orderer admin" echo set -x fabric-ca-client register --caname ca-orderer --id.name ordererAdmin --id.secret ordererAdminpw --id.type admin --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null mkdir -p organizations/ordererOrganizations/example.com/orderers mkdir -p organizations/ordererOrganizations/example.com/orderers/example.com @@ -274,7 +274,7 @@ function createOrderer { echo set -x fabric-ca-client enroll -u https://orderer:ordererpw@localhost:9054 --caname ca-orderer -M ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp --csr.hosts orderer.example.com --csr.hosts localhost --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/config.yaml @@ -283,7 +283,7 @@ function createOrderer { echo set -x fabric-ca-client enroll -u https://orderer:ordererpw@localhost:9054 --caname ca-orderer -M ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls --enrollment.profile tls --csr.hosts orderer.example.com --csr.hosts localhost --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/tlscacerts/* ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/signcerts/* ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt @@ -303,7 +303,7 @@ function createOrderer { echo set -x fabric-ca-client enroll -u https://ordererAdmin:ordererAdminpw@localhost:9054 --caname ca-orderer -M ${PWD}/organizations/ordererOrganizations/example.com/users/Admin@example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - set +x + { set +x; } 2>/dev/null cp ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml ${PWD}/organizations/ordererOrganizations/example.com/users/Admin@example.com/msp/config.yaml diff --git a/test-network/scripts/createChannel.sh b/test-network/scripts/createChannel.sh index 1bf050ae..f23881bf 100755 --- a/test-network/scripts/createChannel.sh +++ b/test-network/scripts/createChannel.sh @@ -22,7 +22,7 @@ createChannelTx() { set -x configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/${CHANNEL_NAME}.tx -channelID $CHANNEL_NAME res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo "Failed to generate channel configuration transaction..." exit 1 @@ -39,7 +39,7 @@ createAncorPeerTx() { set -x configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/${orgmsp}anchors.tx -channelID $CHANNEL_NAME -asOrg ${orgmsp} res=$? - set +x + { set +x; } 2>/dev/null if [ $res -ne 0 ]; then echo "Failed to generate anchor peer update transaction for ${orgmsp}..." exit 1 @@ -58,7 +58,7 @@ createChannel() { set -x peer channel create -o localhost:7050 -c $CHANNEL_NAME --ordererTLSHostnameOverride orderer.example.com -f ./channel-artifacts/${CHANNEL_NAME}.tx --outputBlock ./channel-artifacts/${CHANNEL_NAME}.block --tls --cafile $ORDERER_CA >&log.txt res=$? - set +x + { set +x; } 2>/dev/null let rc=$res COUNTER=$(expr $COUNTER + 1) done @@ -81,7 +81,7 @@ joinChannel() { set -x peer channel join -b ./channel-artifacts/$CHANNEL_NAME.block >&log.txt res=$? - set +x + { set +x; } 2>/dev/null let rc=$res COUNTER=$(expr $COUNTER + 1) done @@ -101,7 +101,7 @@ updateAnchorPeers() { set -x peer channel update -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls --cafile $ORDERER_CA >&log.txt res=$? - set +x + { set +x; } 2>/dev/null let rc=$res COUNTER=$(expr $COUNTER + 1) done diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index 8f674f8a..8491cbeb 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -153,7 +153,7 @@ packageChaincode() { set -x peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME}_${CC_VERSION} >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode packaging on peer0.org${ORG} has failed" echo "===================== Chaincode is packaged on peer0.org${ORG} ===================== " @@ -167,7 +167,7 @@ installChaincode() { set -x peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" echo "===================== Chaincode is installed on peer0.org${ORG} ===================== " @@ -181,7 +181,7 @@ queryInstalled() { set -x peer lifecycle chaincode queryinstalled >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt PACKAGE_ID=$(sed -n "/${CC_NAME}_${CC_VERSION}/{s/^Package ID: //; s/, Label:.*$//; p;}" log.txt) verifyResult $res "Query installed on peer0.org${ORG} has failed" @@ -196,7 +196,7 @@ approveForMyOrg() { set -x peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" echo "===================== Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " @@ -219,7 +219,7 @@ checkCommitReadiness() { set -x peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} --output json >&log.txt res=$? - set +x + { set +x; } 2>/dev/null let rc=0 for var in "$@"; do grep "$var" log.txt &>/dev/null || let rc=1 @@ -249,7 +249,7 @@ commitChaincodeDefinition() { set -x peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name ${CC_NAME} $PEER_CONN_PARMS --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode definition commit failed on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" echo "===================== Chaincode definition committed on channel '$CHANNEL_NAME' ===================== " @@ -272,7 +272,7 @@ queryCommitted() { set -x peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt res=$? - set +x + { set +x; } 2>/dev/null test $res -eq 0 && VALUE=$(cat log.txt | grep -o '^Version: '$CC_VERSION', Sequence: [0-9]*, Endorsement Plugin: escc, Validation Plugin: vscc') test "$VALUE" = "$EXPECTED_RESULT" && let rc=0 COUNTER=$(expr $COUNTER + 1) @@ -303,7 +303,7 @@ chaincodeInvokeInit() { echo invoke fcn call:${fcn_call} peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n ${CC_NAME} $PEER_CONN_PARMS --isInit -c ${fcn_call} >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt verifyResult $res "Invoke execution on $PEERS failed " echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== " @@ -324,7 +324,7 @@ chaincodeQuery() { set -x peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt res=$? - set +x + { set +x; } 2>/dev/null let rc=$res COUNTER=$(expr $COUNTER + 1) done diff --git a/test-network/scripts/org3-scripts/step1org3.sh b/test-network/scripts/org3-scripts/step1org3.sh index 2e0dc0ea..6d2300ba 100755 --- a/test-network/scripts/org3-scripts/step1org3.sh +++ b/test-network/scripts/org3-scripts/step1org3.sh @@ -40,12 +40,12 @@ fetchChannelConfig() { echo "Fetching the most recent configuration block for the channel" set -x peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL --tls --cafile $ORDERER_CA - set +x + { set +x; } 2>/dev/null echo "Decoding config block to JSON and isolating config to ${OUTPUT}" set -x configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config >"${OUTPUT}" - set +x + { set +x; } 2>/dev/null } # createConfigUpdate @@ -64,7 +64,7 @@ createConfigUpdate() { configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate >config_update.json echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope >"${OUTPUT}" - set +x + { set +x; } 2>/dev/null } # signConfigtxAsPeerOrg @@ -75,7 +75,7 @@ signConfigtxAsPeerOrg() { setGlobals $PEERORG set -x peer channel signconfigtx -f "${TX}" - set +x + { set +x; } 2>/dev/null } echo @@ -88,7 +88,7 @@ fetchChannelConfig 1 ${CHANNEL_NAME} config.json # Modify the configuration to append the new org set -x jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json -set +x +{ set +x; } 2>/dev/null # Compute a config update, based on the differences between config.json and modified_config.json, write it as a transaction to org3_update_in_envelope.pb createConfigUpdate ${CHANNEL_NAME} config.json modified_config.json org3_update_in_envelope.pb @@ -107,7 +107,7 @@ echo setGlobals 2 set -x peer channel update -f org3_update_in_envelope.pb -c ${CHANNEL_NAME} -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${ORDERER_CA} -set +x +{ set +x; } 2>/dev/null echo echo "========= Config transaction to add org3 to network submitted! =========== " diff --git a/test-network/scripts/org3-scripts/step2org3.sh b/test-network/scripts/org3-scripts/step2org3.sh index ade5f3f2..7c2f5719 100755 --- a/test-network/scripts/org3-scripts/step2org3.sh +++ b/test-network/scripts/org3-scripts/step2org3.sh @@ -36,7 +36,7 @@ joinChannelWithRetry() { set -x peer channel join -b $CHANNEL_NAME.block >&log.txt res=$? - set +x + { set +x; } 2>/dev/null cat log.txt if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then COUNTER=$(expr $COUNTER + 1) @@ -54,7 +54,7 @@ echo "Fetching channel config block from orderer..." set -x peer channel fetch 0 $CHANNEL_NAME.block -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL_NAME --tls --cafile $ORDERER_CA >&log.txt res=$? -set +x +{ set +x; } 2>/dev/null cat log.txt verifyResult $res "Fetching config block from orderer has Failed" From 69b96f85e03fa0eaf2788954508c0a098dc0cb1e Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 12 Aug 2020 21:50:13 -0500 Subject: [PATCH 8/9] Added javascript chaincode unit tests for asset-transfer-basic Signed-off-by: Paul --- .../chaincode-javascript/.eslintrc.js | 3 +- .../chaincode-javascript/.gitignore | 3 + .../chaincode-javascript/package.json | 4 +- .../test/assetTransfer.test.js | 251 ++++++++++++++++++ 4 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js diff --git a/asset-transfer-basic/chaincode-javascript/.eslintrc.js b/asset-transfer-basic/chaincode-javascript/.eslintrc.js index 6d5751a5..555c0cf5 100644 --- a/asset-transfer-basic/chaincode-javascript/.eslintrc.js +++ b/asset-transfer-basic/chaincode-javascript/.eslintrc.js @@ -5,7 +5,8 @@ module.exports = { env: { node: true, - mocha: true + mocha: true, + es6: true }, parserOptions: { ecmaVersion: 8, diff --git a/asset-transfer-basic/chaincode-javascript/.gitignore b/asset-transfer-basic/chaincode-javascript/.gitignore index 60611a46..eeace290 100644 --- a/asset-transfer-basic/chaincode-javascript/.gitignore +++ b/asset-transfer-basic/chaincode-javascript/.gitignore @@ -5,6 +5,9 @@ # Coverage directory used by tools like istanbul coverage +# Report cache used by istanbul +.nyc_output + # Dependency directories node_modules/ jspm_packages/ diff --git a/asset-transfer-basic/chaincode-javascript/package.json b/asset-transfer-basic/chaincode-javascript/package.json index fabc3180..9cc22242 100644 --- a/asset-transfer-basic/chaincode-javascript/package.json +++ b/asset-transfer-basic/chaincode-javascript/package.json @@ -31,7 +31,9 @@ "nyc": { "exclude": [ "coverage/**", - "test/**" + "test/**", + "index.js", + ".eslintrc.js" ], "reporter": [ "text-summary", diff --git a/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js new file mode 100644 index 00000000..8fefdf24 --- /dev/null +++ b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js @@ -0,0 +1,251 @@ +'use strict'; +const sinon = require('sinon'); +const chai = require('chai'); +const sinonChai = require('sinon-chai'); +const expect = chai.expect; + +const { Context } = require('fabric-contract-api'); +const { ChaincodeStub } = require('fabric-shim'); + +const AssetTransfer = require('../lib/assetTransfer.js'); + +let assert = sinon.assert; +chai.use(sinonChai); + +describe('Asset Transfer Basic Tests', () => { + let transactionContext, chaincodeStub; + beforeEach(() => { + transactionContext = new Context(); + + chaincodeStub = sinon.createStubInstance(ChaincodeStub); + transactionContext.setChaincodeStub(chaincodeStub); + + chaincodeStub.putState.callsFake((key, value) => { + if (!chaincodeStub.states) { + chaincodeStub.states = {}; + } + chaincodeStub.states[key] = value; + }); + + chaincodeStub.getState.callsFake(async (key) => { + let ret; + if (chaincodeStub.states) { + ret = chaincodeStub.states[key]; + } + return Promise.resolve(ret); + }); + + chaincodeStub.deleteState.callsFake(async (key) => { + if (chaincodeStub.states) { + delete chaincodeStub.states[key]; + } + return Promise.resolve(key); + }); + + chaincodeStub.getStateByRange.callsFake(async (start, end) => { + function* internalGetStateByRange() { + if (chaincodeStub.states) { + // Shallow copy + const copied = Object.assign({}, chaincodeStub.states); + + for (let key in copied) { + yield {value: copied[key]}; + } + } + } + + return Promise.resolve(internalGetStateByRange()); + }); + }); + + afterEach(() => { + }); + + describe('Test InitLedger', () => { + it('should return error on InitLedger', async () => { + chaincodeStub.putState.rejects('failed inserting key'); + let assetTransfer = new AssetTransfer(); + try { + await assetTransfer.InitLedger(transactionContext); + assert.fail('InitLedger should have failed'); + } catch (err) { + expect(err.name).to.equal('failed inserting key'); + } + }); + + it('should return success on InitLedger', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.InitLedger(transactionContext); + let ret = JSON.parse((await chaincodeStub.getState('asset6')).toString()); + expect(ret.ID).to.equal('asset6'); + }); + }); + + describe('Test CreateAsset', () => { + it('should return error on CreateAsset', async () => { + chaincodeStub.putState.rejects('failed inserting key'); + + let assetTransfer = new AssetTransfer(); + try { + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + assert.fail('CreateAsset should have failed'); + } catch(err) { + expect(err.name).to.equal('failed inserting key'); + } + }); + + it('should return success on CreateAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString()); + expect(ret.ID).to.equal('asset1'); + expect(ret.Color).to.equal('blue'); + expect(ret.Size).to.equal(5); + expect(ret.Owner).to.equal('Tomoko'); + expect(ret.AppraisedValue).to.equal(300); + }); + }); + + describe('Test ReadAsset', () => { + it('should return error on ReadAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + try { + await assetTransfer.ReadAsset(transactionContext, 'asset2'); + assert.fail('ReadAsset should have failed'); + } catch (err) { + expect(err.message).to.equal('The asset asset2 does not exist'); + } + }); + + it('should return success on ReadAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + let ret = JSON.parse(await chaincodeStub.getState('asset1')); + expect(ret.ID).to.equal('asset1'); + }); + }); + + describe('Test UpdateAsset', () => { + it('should return error on UpdateAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + try { + await assetTransfer.UpdateAsset(transactionContext, 'asset2', 'orange', 10, 'Me', 500); + assert.fail('UpdateAsset should have failed'); + } catch (err) { + expect(err.message).to.equal('The asset asset2 does not exist'); + } + }); + + it('should return success on UpdateAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + await assetTransfer.UpdateAsset(transactionContext, 'asset1', 'orange', 10, 'Me', 500); + let ret = JSON.parse(await chaincodeStub.getState('asset1')); + expect(ret.ID).to.equal('asset1'); + expect(ret.Color).to.equal('orange'); + expect(ret.Size).to.equal(10); + expect(ret.Owner).to.equal('Me'); + expect(ret.AppraisedValue).to.equal(500); + }); + }); + + describe('Test DeleteAsset', () => { + it('should return error on DeleteAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + try { + await assetTransfer.DeleteAsset(transactionContext, 'asset2'); + assert.fail('DeleteAsset should have failed'); + } catch (err) { + expect(err.message).to.equal('The asset asset2 does not exist'); + } + }); + + it('should return success on DeleteAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + await assetTransfer.DeleteAsset(transactionContext, 'asset1'); + let ret = await chaincodeStub.getState('asset1'); + expect(ret).to.equal(undefined); + }); + }); + + describe('Test TransferAsset', () => { + it('should return error on TransferAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + try { + await assetTransfer.TransferAsset(transactionContext, 'asset2', 'Me'); + assert.fail('DeleteAsset should have failed'); + } catch (err) { + assert.pass(); + } + }); + + it('should return success on TransferAsset', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + await assetTransfer.TransferAsset(transactionContext, 'asset1', 'Me'); + let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString()); + expect(ret.Owner).to.equal('Me'); + }); + }); + + describe('Test GetAllAssets', () => { + it('should return error for one key in GetAllAssets', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + + try { + await assetTransfer.TransferAsset(transactionContext, 'asset7', 'Me'); + assert.fail('DeleteAsset should have failed'); + } catch (err) { + assert.pass(); + } + }); + + it('should return success on GetAllAssets', async () => { + let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Robert', 100); + await assetTransfer.CreateAsset(transactionContext, 'asset2', 'orange', 10, 'Paul', 200); + await assetTransfer.CreateAsset(transactionContext, 'asset3', 'red', 15, 'Troy', 300); + await assetTransfer.CreateAsset(transactionContext, 'asset4', 'pink', 20, 'Van', 400); + + let ret = await assetTransfer.GetAllAssets(transactionContext); + ret = JSON.parse(ret); + expect(ret.length).to.equal(4); + }); + + it('should return success on GetAllAssets for non JSON value', async () => { + let assetTransfer = new AssetTransfer(); + + chaincodeStub.putState.onFirstCall().callsFake((key, value) => { + if (!chaincodeStub.states) { + chaincodeStub.states = {}; + } + chaincodeStub.states[key] = 'non-json-value'; + }); + + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Robert', 100); + await assetTransfer.CreateAsset(transactionContext, 'asset2', 'orange', 10, 'Paul', 200); + await assetTransfer.CreateAsset(transactionContext, 'asset3', 'red', 15, 'Troy', 300); + await assetTransfer.CreateAsset(transactionContext, 'asset4', 'pink', 20, 'Van', 400); + + let ret = await assetTransfer.GetAllAssets(transactionContext); + ret = JSON.parse(ret); + expect(ret.length).to.equal(4); + expect(ret[0].Record).to.equal('non-json-value'); + }); + }); +}); \ No newline at end of file From 94faa349304bee9eae423a061fc9f73d863f3032 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 13 Aug 2020 22:16:07 -0500 Subject: [PATCH 9/9] Fixed code review items --- .../test/assetTransfer.test.js | 111 ++++++++++-------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js index 8fefdf24..e9db0328 100644 --- a/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js +++ b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js @@ -13,7 +13,7 @@ let assert = sinon.assert; chai.use(sinonChai); describe('Asset Transfer Basic Tests', () => { - let transactionContext, chaincodeStub; + let transactionContext, chaincodeStub, asset; beforeEach(() => { transactionContext = new Context(); @@ -42,7 +42,7 @@ describe('Asset Transfer Basic Tests', () => { return Promise.resolve(key); }); - chaincodeStub.getStateByRange.callsFake(async (start, end) => { + chaincodeStub.getStateByRange.callsFake(async () => { function* internalGetStateByRange() { if (chaincodeStub.states) { // Shallow copy @@ -56,9 +56,14 @@ describe('Asset Transfer Basic Tests', () => { return Promise.resolve(internalGetStateByRange()); }); - }); - afterEach(() => { + asset = { + ID: 'asset1', + Color: 'blue', + Size: 5, + Owner: 'Tomoko', + AppraisedValue: 300, + }; }); describe('Test InitLedger', () => { @@ -76,8 +81,8 @@ describe('Asset Transfer Basic Tests', () => { it('should return success on InitLedger', async () => { let assetTransfer = new AssetTransfer(); await assetTransfer.InitLedger(transactionContext); - let ret = JSON.parse((await chaincodeStub.getState('asset6')).toString()); - expect(ret.ID).to.equal('asset6'); + let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString()); + expect(ret).to.eql(Object.assign({docType: 'asset'}, asset)); }); }); @@ -87,7 +92,7 @@ describe('Asset Transfer Basic Tests', () => { let assetTransfer = new AssetTransfer(); try { - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); assert.fail('CreateAsset should have failed'); } catch(err) { expect(err.name).to.equal('failed inserting key'); @@ -96,21 +101,18 @@ describe('Asset Transfer Basic Tests', () => { it('should return success on CreateAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); - let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString()); - expect(ret.ID).to.equal('asset1'); - expect(ret.Color).to.equal('blue'); - expect(ret.Size).to.equal(5); - expect(ret.Owner).to.equal('Tomoko'); - expect(ret.AppraisedValue).to.equal(300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); + + let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString()); + expect(ret).to.eql(asset); }); }); describe('Test ReadAsset', () => { it('should return error on ReadAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); try { await assetTransfer.ReadAsset(transactionContext, 'asset2'); @@ -122,17 +124,17 @@ describe('Asset Transfer Basic Tests', () => { it('should return success on ReadAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); - let ret = JSON.parse(await chaincodeStub.getState('asset1')); - expect(ret.ID).to.equal('asset1'); + let ret = JSON.parse(await chaincodeStub.getState(asset.ID)); + expect(ret).to.eql(asset); }); }); describe('Test UpdateAsset', () => { it('should return error on UpdateAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); try { await assetTransfer.UpdateAsset(transactionContext, 'asset2', 'orange', 10, 'Me', 500); @@ -144,22 +146,25 @@ describe('Asset Transfer Basic Tests', () => { it('should return success on UpdateAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); await assetTransfer.UpdateAsset(transactionContext, 'asset1', 'orange', 10, 'Me', 500); - let ret = JSON.parse(await chaincodeStub.getState('asset1')); - expect(ret.ID).to.equal('asset1'); - expect(ret.Color).to.equal('orange'); - expect(ret.Size).to.equal(10); - expect(ret.Owner).to.equal('Me'); - expect(ret.AppraisedValue).to.equal(500); + let ret = JSON.parse(await chaincodeStub.getState(asset.ID)); + let expected = { + ID: 'asset1', + Color: 'orange', + Size: 10, + Owner: 'Me', + AppraisedValue: 500 + }; + expect(ret).to.eql(expected); }); }); describe('Test DeleteAsset', () => { it('should return error on DeleteAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); try { await assetTransfer.DeleteAsset(transactionContext, 'asset2'); @@ -171,10 +176,10 @@ describe('Asset Transfer Basic Tests', () => { it('should return success on DeleteAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); - await assetTransfer.DeleteAsset(transactionContext, 'asset1'); - let ret = await chaincodeStub.getState('asset1'); + await assetTransfer.DeleteAsset(transactionContext, asset.ID); + let ret = await chaincodeStub.getState(asset.ID); expect(ret).to.equal(undefined); }); }); @@ -182,41 +187,30 @@ describe('Asset Transfer Basic Tests', () => { describe('Test TransferAsset', () => { it('should return error on TransferAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); try { await assetTransfer.TransferAsset(transactionContext, 'asset2', 'Me'); assert.fail('DeleteAsset should have failed'); } catch (err) { - assert.pass(); + expect(err.message).to.equal('The asset asset2 does not exist'); } }); it('should return success on TransferAsset', async () => { let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); + await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue); - await assetTransfer.TransferAsset(transactionContext, 'asset1', 'Me'); - let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString()); - expect(ret.Owner).to.equal('Me'); + await assetTransfer.TransferAsset(transactionContext, asset.ID, 'Me'); + let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString()); + expect(ret).to.eql(Object.assign({}, asset, {Owner: 'Me'})); }); }); describe('Test GetAllAssets', () => { - it('should return error for one key in GetAllAssets', async () => { - let assetTransfer = new AssetTransfer(); - await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Tomoko', 300); - - try { - await assetTransfer.TransferAsset(transactionContext, 'asset7', 'Me'); - assert.fail('DeleteAsset should have failed'); - } catch (err) { - assert.pass(); - } - }); - it('should return success on GetAllAssets', async () => { let assetTransfer = new AssetTransfer(); + await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Robert', 100); await assetTransfer.CreateAsset(transactionContext, 'asset2', 'orange', 10, 'Paul', 200); await assetTransfer.CreateAsset(transactionContext, 'asset3', 'red', 15, 'Troy', 300); @@ -225,6 +219,15 @@ describe('Asset Transfer Basic Tests', () => { let ret = await assetTransfer.GetAllAssets(transactionContext); ret = JSON.parse(ret); expect(ret.length).to.equal(4); + + let expected = [ + {Record: {ID: 'asset1', Color: 'blue', Size: 5, Owner: 'Robert', AppraisedValue: 100}}, + {Record: {ID: 'asset2', Color: 'orange', Size: 10, Owner: 'Paul', AppraisedValue: 200}}, + {Record: {ID: 'asset3', Color: 'red', Size: 15, Owner: 'Troy', AppraisedValue: 300}}, + {Record: {ID: 'asset4', Color: 'pink', Size: 20, Owner: 'Van', AppraisedValue: 400}} + ]; + + expect(ret).to.eql(expected); }); it('should return success on GetAllAssets for non JSON value', async () => { @@ -245,7 +248,15 @@ describe('Asset Transfer Basic Tests', () => { let ret = await assetTransfer.GetAllAssets(transactionContext); ret = JSON.parse(ret); expect(ret.length).to.equal(4); - expect(ret[0].Record).to.equal('non-json-value'); + + let expected = [ + {Record: 'non-json-value'}, + {Record: {ID: 'asset2', Color: 'orange', Size: 10, Owner: 'Paul', AppraisedValue: 200}}, + {Record: {ID: 'asset3', Color: 'red', Size: 15, Owner: 'Troy', AppraisedValue: 300}}, + {Record: {ID: 'asset4', Color: 'pink', Size: 20, Owner: 'Van', AppraisedValue: 400}} + ]; + + expect(ret).to.eql(expected); }); }); -}); \ No newline at end of file +});