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