From 5f80da096cb160c047a216d23c583cd357cc2253 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Tue, 7 Jul 2020 16:38:01 -0400 Subject: [PATCH 01/45] Refactor Asset Query Chaincode Into Idiomatic Go Rewrites the chaincod in idiomatic Go and cleans up the general implementation. A future commit should push the chaincode logic itself into a separate package as chaincode cannot be tested when the logic is part of the main package. Signed-off-by: Brett Logan --- .../asset_transfer_ledger_chaincode.go | 372 +++++++++--------- 1 file changed, 180 insertions(+), 192 deletions(-) diff --git a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go index ac6daeb7..c413429c 100644 --- a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go +++ b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go @@ -2,89 +2,94 @@ SPDX-License-Identifier: Apache-2.0 */ -// ====CHAINCODE EXECUTION SAMPLES (CLI) ================== +/* +====CHAINCODE EXECUTION SAMPLES (CLI) ================== -// ==== Invoke assets ==== -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","35","tom"]}' -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","tom"]}' -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","tom"]}' -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}' -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAssetBasedOnColor","blue","jerry"]}' -// peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}' +==== Invoke assets ==== +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","5","tom","35"]}' +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","4","tom","50"]}' +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","6","tom","70"]}' +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}' +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAssetByColor","blue","jerry"]}' +peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}' -// ==== Query assets ==== -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}' -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}' -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}' +==== Query assets ==== +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}' +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}' +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}' -// Rich Query (Only supported if CouchDB is used as state database): -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsByOwner","tom"]}' -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' +Rich Query (Only supported if CouchDB is used as state database): +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsByOwner","tom"]}' +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' -// Rich Query with Pagination (Only supported if CouchDB is used as state database): -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' +Rich Query with Pagination (Only supported if CouchDB is used as state database): +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' -// INDEXES TO SUPPORT COUCHDB RICH QUERIES -// -// Indexes in CouchDB are required in order to make JSON queries efficient and are required for -// any JSON query with a sort. Indexes may be packaged alongside -// chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own -// text file with extension *.json with the index definition formatted in JSON following the -// CouchDB index JSON syntax as documented at: -// http://docs.couchdb.org/en/2.3.1/api/database/find.html#db-index -// -// This asset transfer ledger example chaincode demonstrates a packaged -// index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json. -// -// If you have access to the your peer's CouchDB state database in a development environment, -// you may want to iteratively test various indexes in support of your chaincode queries. You -// can use the CouchDB Fauxton interface or a command line curl utility to create and update -// indexes. Then once you finalize an index, include the index definition alongside your -// chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment -// to managed environments. -// -// In the examples below you can find index definitions that support asset transfer ledger -// chaincode queries, along with the syntax that you can use in development environments -// to create the indexes in the CouchDB Fauxton interface or a curl command line utility. -// +INDEXES TO SUPPORT COUCHDB RICH QUERIES -// Index for docType, owner. -// -// Example curl command line to define index in the CouchDB channel_chaincode database -// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index -// +Indexes in CouchDB are required in order to make JSON queries efficient and are required for +any JSON query with a sort. Indexes may be packaged alongside +chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own +text file with extension *.json with the index definition formatted in JSON following the +CouchDB index JSON syntax as documented at: +http://docs.couchdb.org/en/2.3.1/api/database/find.html#db-index -// Index for docType, owner, size (descending order). -// -// Example curl command line to define index in the CouchDB channel_chaincode database -// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index +This asset transfer ledger example chaincode demonstrates a packaged +index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json. -// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' +If you have access to the your peer's CouchDB state database in a development environment, +you may want to iteratively test various indexes in support of your chaincode queries. You +can use the CouchDB Fauxton interface or a command line curl utility to create and update +indexes. Then once you finalize an index, include the index definition alongside your +chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment +to managed environments. -// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): -// peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' +In the examples below you can find index definitions that support asset transfer ledger +chaincode queries, along with the syntax that you can use in development environments +to create the indexes in the CouchDB Fauxton interface or a curl command line utility. + + +Index for docType, owner. + +Example curl command line to define index in the CouchDB channel_chaincode database +curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index + + +Index for docType, owner, size (descending order). + +Example curl command line to define index in the CouchDB channel_chaincode database: +curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index + +Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' + +Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): +peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' +*/ package main import ( "encoding/json" "fmt" - "strings" + "log" "time" + "github.com/golang/protobuf/ptypes" "github.com/hyperledger/fabric-chaincode-go/shim" "github.com/hyperledger/fabric-contract-api-go/contractapi" ) -// SimpleChaincode example simple Chaincode implementation +const index = "color~name" + +// SimpleChaincode implements the fabric-contract-api-go programming model type SimpleChaincode struct { contractapi.Contract } -type asset struct { - ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database - ID string `json:"ID"` //the fieldtags are needed to keep case from bouncing around +type Asset struct { + DocType string `json:"docType"` //docType is used to distinguish the various types of objects in state database + ID string `json:"ID"` //the field tags are needed to keep case from bouncing around Color string `json:"color"` Size int `json:"size"` Owner string `json:"owner"` @@ -93,69 +98,68 @@ type asset struct { // QueryResult structure used for handling result of query type QueryResult struct { - Record *asset - TxId string `json:"txId"` + Record *Asset + TxId string `json:"txID"` Timestamp time.Time `json:"timestamp"` FetchedRecordsCount int `json:"fetchedRecordsCount"` Bookmark string `json:"bookmark"` } -// CreateAsset - create a new asset, store into chaincode state -func (t *SimpleChaincode) CreateAsset(ctx contractapi.TransactionContextInterface, ID, color, owner string, size, appraisedValue int) error { - - exists, err := t.AssetExists(ctx, ID) +// CreateAsset initializes a new asset in the ledger +func (t *SimpleChaincode) CreateAsset(ctx contractapi.TransactionContextInterface, assetID, color string, size int, owner string, appraisedValue int) error { + exists, err := t.AssetExists(ctx, assetID) if err != nil { - return fmt.Errorf("Failed to get asset: " + err.Error()) - } else if exists { - return fmt.Errorf("This asset already exists: " + ID) + return fmt.Errorf("failed to get asset: %v", err) + } + if exists { + return fmt.Errorf("asset already exists: %s", assetID) } - objectType := "asset" - asset := &asset{ - ObjectType: objectType, - ID: ID, + asset := &Asset{ + DocType: "asset", + ID: assetID, Color: color, Size: size, Owner: owner, AppraisedValue: appraisedValue, } - assetJSON, err := json.Marshal(asset) + assetBytes, err := json.Marshal(asset) if err != nil { return err } - err = ctx.GetStub().PutState(ID, assetJSON) + err = ctx.GetStub().PutState(assetID, assetBytes) if err != nil { return err } - // ==== Index the asset to enable color-based range queries, e.g. return all blue assets ==== - // An 'index' is a normal key/value entry in state. + // Create an index to enable color-based range queries, e.g. return all blue assets. + // An 'index' is a normal key-value entry in the ledger. // The key is a composite key, with the elements that you want to range query on listed first. // In our case, the composite key is based on indexName~color~name. // This will enable very efficient state range queries based on composite keys matching indexName~color~* - indexName := "color~name" - colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{asset.Color, asset.ID}) + colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(index, []string{asset.Color, asset.ID}) if err != nil { return err } - // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the asset. + // Save index entry to world state. Only the key name is needed, no need to store a duplicate copy of the asset. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value value := []byte{0x00} return ctx.GetStub().PutState(colorNameIndexKey, value) } -// ReadAsset - read a asset from chaincode state -func (t *SimpleChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, ID string) (*asset, error) { - assetJSON, err := ctx.GetStub().GetState(ID) +// ReadAsset retrieves an asset from the ledger +func (t *SimpleChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) { + assetBytes, err := ctx.GetStub().GetState(assetID) if err != nil { - return nil, err - } else if assetJSON == nil { - return nil, fmt.Errorf("%s does not exist", ID) + return nil, fmt.Errorf("failed to get asset %s: %v", assetID, err) + } + if assetBytes == nil { + return nil, fmt.Errorf("asset %s does not exist", assetID) } - asset := new(asset) - err = json.Unmarshal(assetJSON, asset) + var asset *Asset + err = json.Unmarshal(assetBytes, &asset) if err != nil { return nil, err } @@ -163,83 +167,61 @@ func (t *SimpleChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, return asset, nil } -// DeleteAsset - remove a asset key/value pair from state +// DeleteAsset removes an asset key-value pair from the ledger func (t *SimpleChaincode) DeleteAsset(ctx contractapi.TransactionContextInterface, assetID string) error { - - // to maintain the color~name index, we need to read the asset first and get its color - assetJSON, err := ctx.GetStub().GetState(assetID) //get the asset from chaincode state + asset, err := t.ReadAsset(ctx, assetID) if err != nil { - return fmt.Errorf("Failed to get state for %s", assetID) - } else if assetJSON == nil { - return fmt.Errorf("Asset does not exist %s", assetID) + return err } - var asset asset - err = json.Unmarshal([]byte(assetJSON), &asset) + err = ctx.GetStub().DelState(assetID) if err != nil { - return fmt.Errorf("Failed to decode JSON of %s", assetID) + return fmt.Errorf("failed to delete asset %s: %v", assetID, err) } - err = ctx.GetStub().DelState(assetID) //remove the asset from chaincode state + colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(index, []string{asset.Color, asset.ID}) if err != nil { - return fmt.Errorf("Failed to delete state:" + err.Error()) + return err } - // maintain the index - indexName := "color~name" - colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{asset.Color, asset.ID}) - if err != nil { - return fmt.Errorf(err.Error()) - } - - // Delete index entry to state. + // Delete index entry return ctx.GetStub().DelState(colorNameIndexKey) } -// TransferAsset transfers a asset by setting a new owner name on the asset +// TransferAsset transfers an asset by setting a new owner name on the asset func (t *SimpleChaincode) TransferAsset(ctx contractapi.TransactionContextInterface, assetID, newOwner string) error { - newOwner = strings.ToLower(newOwner) - - assetAsBytes, err := ctx.GetStub().GetState(assetID) + asset, err := t.ReadAsset(ctx, assetID) if err != nil { - return fmt.Errorf("Failed to get asset:" + err.Error()) - } else if assetAsBytes == nil { - return fmt.Errorf("Asset does not exist") + return err } - assetToTransfer := asset{} - err = json.Unmarshal(assetAsBytes, &assetToTransfer) + asset.Owner = newOwner + assetBytes, err := json.Marshal(asset) if err != nil { - return fmt.Errorf(err.Error()) + return err } - assetToTransfer.Owner = newOwner //change the owner - assetJSON, _ := json.Marshal(assetToTransfer) - return ctx.GetStub().PutState(assetID, assetJSON) + return ctx.GetStub().PutState(assetID, assetBytes) } -// constructQueryResponseFromIterator constructs a JSON array containing query results from -// a given result iterator +// constructQueryResponseFromIterator constructs a slice of query results from the resultsIterator func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) ([]*QueryResult, error) { - - resp := []*QueryResult{} - + var results []*QueryResult for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } - newRecord := new(QueryResult) - err = json.Unmarshal(queryResponse.Value, newRecord) + var result *QueryResult + err = json.Unmarshal(queryResponse.Value, &result) if err != nil { return nil, err } - - resp = append(resp, newRecord) + results = append(results, result) } - return resp, nil + return results, nil } // GetAssetsByRange performs a range query based on the start and end keys provided. @@ -260,48 +242,46 @@ func (t *SimpleChaincode) GetAssetsByRange(ctx contractapi.TransactionContextInt return constructQueryResponseFromIterator(resultsIterator) } -// TransferAssetBasedOnColor will transfer assets of a given color to a certain new owner. -// Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'. +// TransferAssetByColor will transfer assets of a given color to a certain new owner. +// Uses 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 -func (t *SimpleChaincode) TransferAssetBasedOnColor(ctx contractapi.TransactionContextInterface, color, newOwner string) error { - newOwner = strings.ToLower(newOwner) - - // Query the color~name index by color - // This will execute a key range query on all keys starting with 'color' - coloredAssetResultsIterator, err := ctx.GetStub().GetStateByPartialCompositeKey("color~name", []string{color}) +func (t *SimpleChaincode) TransferAssetByColor(ctx contractapi.TransactionContextInterface, color, newOwner string) error { + // Execute a key range query on all keys starting with 'color' + coloredAssetResultsIterator, err := ctx.GetStub().GetStateByPartialCompositeKey(index, []string{color}) if err != nil { - return fmt.Errorf(err.Error()) + return err } defer coloredAssetResultsIterator.Close() - // Iterate through result set and for each asset found, transfer to newOwner - var i int - for i = 0; coloredAssetResultsIterator.HasNext(); i++ { - // Note that we don't get the value (2nd return variable), we'll just get the asset name from the composite key + for coloredAssetResultsIterator.HasNext() { responseRange, err := coloredAssetResultsIterator.Next() if err != nil { - return fmt.Errorf(err.Error()) + return err } - // get the color and name from color~name composite key _, compositeKeyParts, err := ctx.GetStub().SplitCompositeKey(responseRange.Key) if err != nil { - return fmt.Errorf(err.Error()) + return err } if len(compositeKeyParts) > 1 { returnedAssetID := compositeKeyParts[1] - - // Now call the transfer function for the found asset. - // Re-use the same function that is used to transfer individual assets - err = t.TransferAsset(ctx, returnedAssetID, newOwner) - // if the transfer failed break out of loop and return error + asset, err := t.ReadAsset(ctx, returnedAssetID) if err != nil { - return fmt.Errorf("Transfer failed: %v", err) + return err + } + asset.Owner = newOwner + assetBytes, err := json.Marshal(asset) + if err != nil { + return err + } + err = ctx.GetStub().PutState(returnedAssetID, assetBytes) + if err != nil { + return fmt.Errorf("transfer failed for asset %s: %v", returnedAssetID, err) } } } @@ -309,14 +289,13 @@ func (t *SimpleChaincode) TransferAssetBasedOnColor(ctx contractapi.TransactionC return nil } -// QueryAssetsByOwner queries for assets based on a passed in owner. +// QueryAssetsByOwner queries for assets based on the owners name. // 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 func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]*QueryResult, error) { - queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"asset\",\"owner\":\"%s\"}}", owner) - + queryString := fmt.Sprintf(`{"selector":{"docType":"asset","owner":"%s"}}`, owner) return getQueryResultForQueryString(ctx, queryString) } @@ -331,9 +310,8 @@ func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterfac } // getQueryResultForQueryString executes the passed in query string. -// Result set is built and returned as a byte array containing the JSON results. +// The result set is built and returned as a byte array containing the JSON results. func getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*QueryResult, error) { - resultsIterator, err := ctx.GetStub().GetQueryResult(queryString) if err != nil { return nil, err @@ -343,13 +321,17 @@ func getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, q return constructQueryResponseFromIterator(resultsIterator) } -// GetAssetsByRangeWithPagination performs a range query based on the start & end key, +// GetAssetsByRangeWithPagination performs a range query based on the start and 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. // Example: Pagination with Range Query -func (t *SimpleChaincode) GetAssetsByRangeWithPagination(ctx contractapi.TransactionContextInterface, startKey, - endKey, bookmark string, pageSize int) ([]*QueryResult, error) { +func (t *SimpleChaincode) GetAssetsByRangeWithPagination( + ctx contractapi.TransactionContextInterface, + startKey, + endKey, + bookmark string, + pageSize int) ([]*QueryResult, error) { resultsIterator, _, err := ctx.GetStub().GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark) if err != nil { @@ -368,14 +350,22 @@ func (t *SimpleChaincode) GetAssetsByRangeWithPagination(ctx contractapi.Transac // Only available on state databases that support rich query (e.g. CouchDB) // Paginated queries are only valid for read only transactions. // Example: Pagination with Ad hoc Rich Query -func (t *SimpleChaincode) QueryAssetsWithPagination(ctx contractapi.TransactionContextInterface, queryString, - bookmark string, pageSize int) ([]*QueryResult, error) { +func (t *SimpleChaincode) QueryAssetsWithPagination( + ctx contractapi.TransactionContextInterface, + queryString, + bookmark string, + pageSize int) ([]*QueryResult, error) { + return getQueryResultForQueryStringWithPagination(ctx, queryString, int32(pageSize), bookmark) } // getQueryResultForQueryStringWithPagination executes the passed in query string with -// pagination info. Result set is built and returned as a byte array containing the JSON results. -func getQueryResultForQueryStringWithPagination(ctx contractapi.TransactionContextInterface, queryString string, pageSize int32, bookmark string) ([]*QueryResult, error) { +// pagination info. The result set is built and returned as a byte array containing the JSON results. +func getQueryResultForQueryStringWithPagination( + ctx contractapi.TransactionContextInterface, + queryString string, + pageSize int32, + bookmark string) ([]*QueryResult, error) { resultsIterator, _, err := ctx.GetStub().GetQueryResultWithPagination(queryString, pageSize, bookmark) if err != nil { @@ -388,30 +378,32 @@ func getQueryResultForQueryStringWithPagination(ctx contractapi.TransactionConte // GetAssetHistory returns the chain of custody for an asset since issuance. func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInterface, assetID string) ([]QueryResult, error) { - resultsIterator, err := ctx.GetStub().GetHistoryForKey(assetID) if err != nil { return nil, err } defer resultsIterator.Close() - records := []QueryResult{} - + var records []QueryResult for resultsIterator.HasNext() { response, err := resultsIterator.Next() if err != nil { return nil, err } - asset := new(asset) - err = json.Unmarshal(response.Value, asset) + var asset *Asset + err = json.Unmarshal(response.Value, &asset) if err != nil { return nil, err } + timestamp, err := ptypes.Timestamp(response.Timestamp) + if err != nil { + return nil, err + } record := QueryResult{ TxId: response.TxId, - Timestamp: time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)), + Timestamp: timestamp, Record: asset, } records = append(records, record) @@ -420,36 +412,36 @@ func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInte return records, nil } -// AssetExists returns true when asset with given ID exists in world state -func (t *SimpleChaincode) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { - assetJSON, err := ctx.GetStub().GetState(id) +// AssetExists returns true when asset with given ID exists in the ledger. +func (t *SimpleChaincode) AssetExists(ctx contractapi.TransactionContextInterface, assetID string) (bool, error) { + assetBytes, err := ctx.GetStub().GetState(assetID) if err != nil { - return false, fmt.Errorf("Failed to read from world state. %s", err.Error()) + return false, fmt.Errorf("failed to read asset %s from world state. %v", assetID, err) } - return assetJSON != nil, nil + return assetBytes != nil, nil } -// InitLedger creates sample assets in the ledger +// InitLedger creates the initial set of assets in the ledger. func (t *SimpleChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error { - assets := []asset{ - asset{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, - asset{ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, - asset{ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, - asset{ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, - asset{ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, - asset{ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, + assets := []Asset{ + {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, + {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, + {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, + {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, + {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, + {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, } for _, asset := range assets { - assetJSON, err := json.Marshal(asset) + assetBytes, err := json.Marshal(asset) if err != nil { return err } - err = ctx.GetStub().PutState(asset.ID, assetJSON) + err = ctx.GetStub().PutState(asset.ID, assetBytes) if err != nil { - return fmt.Errorf("Failed to put to world state. %s", err.Error()) + return fmt.Errorf("failed to put to world state. %v", err) } } @@ -457,16 +449,12 @@ func (t *SimpleChaincode) InitLedger(ctx contractapi.TransactionContextInterface } func main() { - - chaincode, err := contractapi.NewChaincode(new(SimpleChaincode)) - + chaincode, err := contractapi.NewChaincode(&SimpleChaincode{}) if err != nil { - fmt.Printf("Error creating asset chaincode: %s", err.Error()) - return + log.Panicf("Error creating asset chaincode: %v", err) } if err := chaincode.Start(); err != nil { - fmt.Printf("Error starting asset chaincode: %s", err.Error()) - return + log.Panicf("Error starting asset chaincode: %v", err) } } From 1f27c327f3afb88d6933edee850155eb6831606c Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Wed, 24 Jun 2020 10:18:31 -0400 Subject: [PATCH 02/45] Allow install of a chaincode options on the network script to deploy chaincode -ccn The name to be used as the deployed name and used as the short name of known chaincodes when the -ccp is not included. known short names 'basic' - asset-transfer-basic 'secure' - asset-transfer-secured-agreement 'ledger' - asset-transfer-ledger-queries 'private' - asset-transfer-private-data -ccl the language of the chaincode -ccv the version -ccs the sequence -ccp [optional] the path to the chaincode, when provided the -ccn will be the deployed name -cci [optional] the chaincode function to call during deployment that will perform an initialization of the channel state required for this chaincode Signed-off-by: Bret Harrison --- ci/templates/test-network/azure-pipelines.yml | 5 +- fabcar/startFabric.sh | 18 +- test-network/.gitignore | 2 +- test-network/README.md | 2 +- test-network/network.sh | 93 ++-- test-network/scripts/deployCC.sh | 396 ++++++++++-------- test-network/scripts/envVar.sh | 2 +- .../scripts/org3-scripts/envVarCLI.sh | 2 +- 8 files changed, 306 insertions(+), 214 deletions(-) diff --git a/ci/templates/test-network/azure-pipelines.yml b/ci/templates/test-network/azure-pipelines.yml index f4a3edc2..e934455c 100644 --- a/ci/templates/test-network/azure-pipelines.yml +++ b/ci/templates/test-network/azure-pipelines.yml @@ -5,7 +5,10 @@ steps: - script: | ./network.sh up createChannel -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml - ./network.sh deployCC -l javascript + ./network.sh deployCC -ccn basic -ccv 1 -ccl javascript -cci initLedger + ./network.sh deployCC -ccn basic -ccv 2 -ccl golang -cci initLedger + ./network.sh deployCC -ccn basic -ccv 3 -ccl typescript -cci initLedger + ./network.sh deployCC -ccn secure -ccv 1 -ccl golang ./network.sh down workingDirectory: test-network displayName: Start Test Network diff --git a/fabcar/startFabric.sh b/fabcar/startFabric.sh index 5f1452cd..8ce18cb3 100755 --- a/fabcar/startFabric.sh +++ b/fabcar/startFabric.sh @@ -12,13 +12,19 @@ export MSYS_NO_PATHCONV=1 starttime=$(date +%s) CC_SRC_LANGUAGE=${1:-"go"} CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` -if [ "$CC_SRC_LANGUAGE" != "go" -a "$CC_SRC_LANGUAGE" != "golang" -a "$CC_SRC_LANGUAGE" != "java" \ - -a "$CC_SRC_LANGUAGE" != "javascript" -a "$CC_SRC_LANGUAGE" != "typescript" ] ; then +if [ "$CC_SRC_LANGUAGE" = "go" -o "$CC_SRC_LANGUAGE" = "golang" ] ; then + CC_SRC_PATH="../chaincode/fabcar/go/" +elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then + CC_SRC_PATH="../chaincode/fabcar/javascript/" +elif [ "$CC_SRC_LANGUAGE" = "java" ]; then + CC_SRC_PATH="../chaincode/fabcar/java" +elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="../chaincode/fabcar/typescript/" +else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, javascript, and typescript - exit 1 - + echo Supported chaincode languages are: go, java, javascript, and typescript + exit 1 fi # clean out any old identites in the wallets @@ -31,7 +37,7 @@ rm -rf go/wallet/* pushd ../test-network ./network.sh down ./network.sh up createChannel -ca -s couchdb -./network.sh deployCC -l ${CC_SRC_LANGUAGE} +./network.sh deployCC -ccn fabcar -ccv 1 -cci initLedger -ccl ${CC_SRC_LANGUAGE} -ccp ${CC_SRC_PATH} popd cat < [Flags]" - echo " " - echo " - 'up' - bring up fabric orderer and peer nodes. No channel is created" - echo " - 'up createChannel' - bring up fabric network with one channel" - echo " - 'createChannel' - create and join a channel after the network is created" - echo " - 'deployCC' - deploy the fabcar chaincode on the channel" - echo " - 'down' - clear the network with docker-compose down" - echo " - 'restart' - restart the network" + echo " Modes:" + echo " "$'\e[0;32m'up$'\e[0m' - bring up fabric orderer and peer nodes. No channel is created + echo " "$'\e[0;32m'up createChannel$'\e[0m' - bring up fabric network with one channel + echo " "$'\e[0;32m'createChannel$'\e[0m' - create and join a channel after the network is created + echo " "$'\e[0;32m'deployCC$'\e[0m' - deploy the asset transfer basic chaincode on the channel or specify + echo " "$'\e[0;32m'down$'\e[0m' - clear the network with docker-compose down + echo " "$'\e[0;32m'restart$'\e[0m' - restart the network echo echo " Flags:" echo " -ca - create Certificate Authorities to generate the crypto material" @@ -34,26 +34,31 @@ function printHelp() { echo " -s - the database backend to use: goleveldb (default) or couchdb" echo " -r - CLI times out after certain number of attempts (defaults to 5)" echo " -d - delay duration in seconds (defaults to 3)" - echo " -l - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" - echo " -v - chaincode version. Must be a round number, 1, 2, 3, etc" + echo " -ccn - the short name of the chaincode to deploy: basic (default),ledger, private, secured" + echo " -ccl - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" + echo " -ccv - chaincode version. 1.0 (default)" + echo " -ccs - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc" + echo " -ccp - Optional, chaincode path. Path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." + echo " -cci - Optional, chaincode init required function to invoke. When provided this function will be invoked after deployment of the chaincode and will define the chaincode as initialization required." echo " -i - the tag to be used to launch the network (defaults to \"latest\")" echo " -cai - the image tag to be used for CA (defaults to \"${CA_IMAGETAG}\")" echo " -verbose - verbose mode" - echo " network.sh -h (print this message)" + echo " -h - print this message" echo - echo " Possible Mode and flags" - echo " network.sh up -ca -c -r -d -s -i -verbose" - echo " network.sh up createChannel -ca -c -r -d -s -i -verbose" - echo " network.sh createChannel -c -r -d -verbose" - echo " network.sh deployCC -l -v -r -d -verbose" + echo " Possible Mode and flag combinations" + echo " "$'\e[0;32m'up$'\e[0m' -ca -c -r -d -s -i -verbose + echo " "$'\e[0;32m'up createChannel$'\e[0m' -ca -c -r -d -s -i -verbose + echo " "$'\e[0;32m'createChannel$'\e[0m' -c -r -d -verbose + echo " "$'\e[0;32m'deployCC$'\e[0m' -ccn -ccl -ccv -ccs -ccp -cci -r -d -verbose echo echo " Taking all defaults:" - echo " network.sh up" + echo " network.sh up" echo echo " Examples:" - echo " network.sh up createChannel -ca -c mychannel -s couchdb -i 2.0.0" - echo " network.sh createChannel -c channelName" - echo " network.sh deployCC -l javascript" + echo " network.sh up createChannel -ca -c mychannel -s couchdb -i 2.0.0" + echo " network.sh createChannel -c channelName" + echo " network.sh deployCC -ccn basic -ccl javascript" + echo " network.sh deployCC -ccn mychaincode -ccp ./user/mychaincode -ccv 1 -ccl javascript" } # Obtain CONTAINER_IDS and remove them @@ -81,7 +86,7 @@ function removeUnwantedImages() { } # Versions of fabric known not to work with the test network -BLACKLISTED_VERSIONS="^1\.0\. ^1\.1\. ^1\.2\. ^1\.3\. ^1\.4\." +NONWORKING_VERSIONS="^1\.0\. ^1\.1\. ^1\.2\. ^1\.3\. ^1\.4\." # Do some basic sanity checking to make sure that the appropriate versions of fabric # binaries/images are available. In the future, additional checking for the presence @@ -112,7 +117,7 @@ function checkPrereqs() { echo "===============================================" fi - for UNSUPPORTED_VERSION in $BLACKLISTED_VERSIONS; do + for UNSUPPORTED_VERSION in $NONWORKING_VERSIONS; do echo "$LOCAL_VERSION" | grep -q $UNSUPPORTED_VERSION if [ $? -eq 0 ]; then echo "ERROR! Local Fabric binary version of $LOCAL_VERSION does not match the versions supported by the test network." @@ -205,7 +210,7 @@ function createOrgs() { res=$? set +x if [ $res -ne 0 ]; then - echo "Failed to generate certificates..." + echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi @@ -218,7 +223,7 @@ function createOrgs() { res=$? set +x if [ $res -ne 0 ]; then - echo "Failed to generate certificates..." + echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi @@ -231,7 +236,7 @@ function createOrgs() { res=$? set +x if [ $res -ne 0 ]; then - echo "Failed to generate certificates..." + echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi @@ -320,7 +325,7 @@ function createConsortium() { res=$? set +x if [ $res -ne 0 ]; then - echo "Failed to generate orderer genesis block..." + echo $'\e[1;32m'"Failed to generate orderer genesis block..."$'\e[0m' exit 1 fi } @@ -381,7 +386,7 @@ function createChannel() { ## Call the script to isntall and instantiate a chaincode on the channel function deployCC() { - scripts/deployCC.sh $CHANNEL_NAME $CC_SRC_LANGUAGE $VERSION $CLI_DELAY $MAX_RETRY $VERBOSE + scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CLI_DELAY $MAX_RETRY $VERBOSE if [ $? -ne 0 ]; then echo "ERROR !!! Deploying chaincode failed" @@ -431,6 +436,12 @@ MAX_RETRY=5 CLI_DELAY=3 # channel name defaults to "mychannel" CHANNEL_NAME="mychannel" +# chaincode name defaults to "basic" +CC_NAME="basic" +# chaincode path defaults to "NA" +CC_SRC_PATH="NA" +# chaincode init function defaults to "NA" +CC_INIT_FCN="NA" # use this as the default docker-compose yaml definition COMPOSE_FILE_BASE=docker/docker-compose-test-net.yaml # docker-compose.yaml file if you are using couchdb @@ -442,10 +453,12 @@ COMPOSE_FILE_COUCH_ORG3=addOrg3/docker/docker-compose-couch-org3.yaml # use this as the default docker-compose yaml definition for org3 COMPOSE_FILE_ORG3=addOrg3/docker/docker-compose-org3.yaml # -# use golang as the default language for chaincode -CC_SRC_LANGUAGE=golang +# use go as the default language for chaincode +CC_SRC_LANGUAGE="go" # Chaincode version -VERSION=1 +CC_VERSION="1.0" +# Chaincode definition sequence +CC_SEQUENCE=1 # default image tag IMAGETAG="latest" # default ca image tag @@ -501,12 +514,28 @@ while [[ $# -ge 1 ]] ; do DATABASE="$2" shift ;; - -l ) + -ccl ) CC_SRC_LANGUAGE="$2" shift ;; - -v ) - VERSION="$2" + -ccn ) + CC_NAME="$2" + shift + ;; + -ccv ) + CC_VERSION="$2" + shift + ;; + -ccs ) + CC_SEQUENCE="$2" + shift + ;; + -ccp ) + CC_SRC_PATH="$2" + shift + ;; + -cci ) + CC_INIT_FCN="$2" shift ;; -i ) diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index 926f7be6..e94afd66 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -1,50 +1,94 @@ +CHANNEL_NAME=${1:-"mychannel"} +CC_NAME=${2:-"basic"} +CC_SRC_PATH=${3:-"NA"} +CC_SRC_LANGUAGE=${4:-"go"} +CC_VERSION=${5:-"1.0"} +CC_SEQUENCE=${6:-"1"} +CC_INIT_FCN=${7:-"NA"} +DELAY=${8:-"3"} +MAX_RETRY=${9:-"5"} +VERBOSE=${10:-"false"} + +echo --- executing with the following +echo - CHANNEL_NAME:$'\e[0;32m'$CHANNEL_NAME$'\e[0m' +echo - CC_NAME:$'\e[0;32m'$CC_NAME$'\e[0m' +echo - CC_SRC_PATH:$'\e[0;32m'$CC_SRC_PATH$'\e[0m' +echo - CC_SRC_LANGUAGE:$'\e[0;32m'$CC_SRC_LANGUAGE$'\e[0m' +echo - CC_VERSION:$'\e[0;32m'$CC_VERSION$'\e[0m' +echo - CC_SEQUENCE:$'\e[0;32m'$CC_SEQUENCE$'\e[0m' +echo - CC_INIT_FCN:$'\e[0;32m'$CC_INIT_FCN$'\e[0m' +echo - DELAY:$'\e[0;32m'$DELAY$'\e[0m' +echo - MAX_RETRY:$'\e[0;32m'$MAX_RETRY$'\e[0m' +echo - VERBOSE:$'\e[0;32m'$VERBOSE$'\e[0m' -CHANNEL_NAME="$1" -CC_SRC_LANGUAGE="$2" -VERSION="$3" -DELAY="$4" -MAX_RETRY="$5" -VERBOSE="$6" -: ${CHANNEL_NAME:="mychannel"} -: ${CC_SRC_LANGUAGE:="golang"} -: ${VERSION:="1"} -: ${DELAY:="3"} -: ${MAX_RETRY:="5"} -: ${VERBOSE:="false"} CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` FABRIC_CFG_PATH=$PWD/../config/ -if [ "$CC_SRC_LANGUAGE" = "go" -o "$CC_SRC_LANGUAGE" = "golang" ] ; then - CC_RUNTIME_LANGUAGE=golang - CC_SRC_PATH="../chaincode/fabcar/go/" +# User has not provided a path, therefore the CC_NAME must +# be the short name of a known chaincode sample +if [ "$CC_SRC_PATH" = "NA" ]; then + echo Determining the path to the chaincode + # first see which chaincode we have. This will be based on the + # short name of the known chaincode sample + if [ "$CC_NAME" = "basic" ]; then + echo $'\e[0;32m'asset-transfer-basic$'\e[0m' chaincode + CC_SRC_PATH="../asset-transfer-basic" + elif [ "$CC_NAME" = "secure" ]; then + echo $'\e[0;32m'asset-transfer-secured-agreeement$'\e[0m' chaincode + CC_SRC_PATH="../asset-transfer-secured-agreement" + elif [ "$CC_NAME" = "ledger" ]; then + echo $'\e[0;32m'asset-transfer-secured-agreeement$'\e[0m' chaincode + CC_SRC_PATH="../asset-transfer-ledger-queries" + elif [ "$CC_NAME" = "private" ]; then + echo $'\e[0;32m'asset-transfer-private-data$'\e[0m' chaincode + CC_SRC_PATH="../asset-transfer-private-data" + else + echo The chaincode name ${CC_NAME} is not supported by this script + echo Supported chaincode names are: basic, secure, and private + exit 1 + fi - echo Vendoring Go dependencies ... - pushd ../chaincode/fabcar/go + # now see what language it is written in + if [ "$CC_SRC_LANGUAGE" = "go" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-go/" + elif [ "$CC_SRC_LANGUAGE" = "java" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-java/" + elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-javascript/" + elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-typescript/" + fi +fi + +# do some language specific preparation to the chaincode before packaging +if [ "$CC_SRC_LANGUAGE" = "go" ]; then + CC_RUNTIME_LANGUAGE=golang + + echo Vendoring Go dependencies at $CC_SRC_PATH + pushd $CC_SRC_PATH GO111MODULE=on go mod vendor popd echo Finished vendoring Go dependencies -elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then - CC_RUNTIME_LANGUAGE=node # chaincode runtime language is node.js - CC_SRC_PATH="../chaincode/fabcar/javascript/" - elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_RUNTIME_LANGUAGE=java - CC_SRC_PATH="../chaincode/fabcar/java/build/install/fabcar" echo Compiling Java code ... - pushd ../chaincode/fabcar/java + pushd $CC_SRC_PATH ./gradlew installDist popd echo Finished compiling Java code + CC_SRC_PATH=$CC_SRC_PATH/build/install/$CC_NAME + +elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then + CC_RUNTIME_LANGUAGE=node elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then - CC_RUNTIME_LANGUAGE=node # chaincode runtime language is node.js - CC_SRC_PATH="../chaincode/fabcar/typescript/" + CC_RUNTIME_LANGUAGE=node echo Compiling TypeScript code into JavaScript ... - pushd ../chaincode/fabcar/typescript + pushd $CC_SRC_PATH npm install npm run build popd @@ -56,200 +100,210 @@ else exit 1 fi +INIT_REQUIRED="--init-required" +# check if the init fcn should be called +if [ "$CC_INIT_FCN" = "NA" ]; then + INIT_REQUIRED="" +fi + # import utils . scripts/envVar.sh packageChaincode() { - ORG=$1 - setGlobals $ORG - set -x - peer lifecycle chaincode package fabcar.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label fabcar_${VERSION} >&log.txt - res=$? - set +x - cat log.txt - verifyResult $res "Chaincode packaging on peer0.org${ORG} has failed" - echo "===================== Chaincode is packaged on peer0.org${ORG} ===================== " - echo + ORG=$1 + setGlobals $ORG + 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 + cat log.txt + verifyResult $res "Chaincode packaging on peer0.org${ORG} has failed" + echo "===================== Chaincode is packaged on peer0.org${ORG} ===================== " + echo } # installChaincode PEER ORG installChaincode() { - ORG=$1 - setGlobals $ORG - set -x - peer lifecycle chaincode install fabcar.tar.gz >&log.txt - res=$? - set +x - cat log.txt - verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" - echo "===================== Chaincode is installed on peer0.org${ORG} ===================== " - echo + ORG=$1 + setGlobals $ORG + set -x + peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt + res=$? + set +x + cat log.txt + verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" + echo "===================== Chaincode is installed on peer0.org${ORG} ===================== " + echo } # queryInstalled PEER ORG queryInstalled() { - ORG=$1 - setGlobals $ORG - set -x - peer lifecycle chaincode queryinstalled >&log.txt - res=$? - set +x - cat log.txt - PACKAGE_ID=$(sed -n "/fabcar_${VERSION}/{s/^Package ID: //; s/, Label:.*$//; p;}" log.txt) - verifyResult $res "Query installed on peer0.org${ORG} has failed" - echo "===================== Query installed successful on peer0.org${ORG} on channel ===================== " - echo + ORG=$1 + setGlobals $ORG + set -x + peer lifecycle chaincode queryinstalled >&log.txt + res=$? + set +x + 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" + echo "===================== Query installed successful on peer0.org${ORG} on channel ===================== " + echo } # approveForMyOrg VERSION PEER ORG approveForMyOrg() { - ORG=$1 - setGlobals $ORG - set -x - peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name fabcar --version ${VERSION} --init-required --package-id ${PACKAGE_ID} --sequence ${VERSION} >&log.txt - set +x - 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' ===================== " - echo + ORG=$1 + setGlobals $ORG + 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} >&log.txt + set +x + 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' ===================== " + echo } # checkCommitReadiness VERSION PEER ORG checkCommitReadiness() { - ORG=$1 - shift 1 - setGlobals $ORG - echo "===================== Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + ORG=$1 + shift 1 + setGlobals $ORG + echo "===================== Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll - # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do - sleep $DELAY - echo "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." - set -x - peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name fabcar --version ${VERSION} --sequence ${VERSION} --output json --init-required >&log.txt - res=$? - set +x - let rc=0 - for var in "$@" - do - grep "$var" log.txt &>/dev/null || let rc=1 - done + # we either get a successful response, or reach MAX RETRY + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + sleep $DELAY + echo "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." + set -x + peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} --output json >&log.txt + res=$? + set +x + let rc=0 + for var in "$@"; do + grep "$var" log.txt &>/dev/null || let rc=1 + done COUNTER=$(expr $COUNTER + 1) done - cat log.txt - if test $rc -eq 0; then - echo "===================== Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " - else - echo "!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Check commit readiness result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!" - echo - exit 1 - fi + cat log.txt + if test $rc -eq 0; then + echo "===================== Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + else + echo + echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Check commit readiness result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' + echo + exit 1 + fi } # commitChaincodeDefinition VERSION PEER ORG (PEER ORG)... commitChaincodeDefinition() { - parsePeerConnectionParameters $@ - res=$? - verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " + parsePeerConnectionParameters $@ + res=$? + verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " - # while 'peer chaincode' command can get the orderer endpoint from the - # peer (if join was successful), let's supply it directly as we know - # it using the "-o" option - set -x - peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name fabcar $PEER_CONN_PARMS --version ${VERSION} --sequence ${VERSION} --init-required >&log.txt - res=$? - set +x - 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' ===================== " - echo + # while 'peer chaincode' command can get the orderer endpoint from the + # peer (if join was successful), let's supply it directly as we know + # it using the "-o" option + 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} >&log.txt + res=$? + set +x + 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' ===================== " + echo } # queryCommitted ORG queryCommitted() { - ORG=$1 - setGlobals $ORG - EXPECTED_RESULT="Version: ${VERSION}, Sequence: ${VERSION}, Endorsement Plugin: escc, Validation Plugin: vscc" - echo "===================== Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + ORG=$1 + setGlobals $ORG + EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc" + echo "===================== Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll - # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do - sleep $DELAY - echo "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds." - set -x - peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name fabcar >&log.txt - res=$? - set +x - test $res -eq 0 && VALUE=$(cat log.txt | grep -o '^Version: [0-9], Sequence: [0-9], Endorsement Plugin: escc, Validation Plugin: vscc') - test "$VALUE" = "$EXPECTED_RESULT" && let rc=0 + # we either get a successful response, or reach MAX RETRY + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + sleep $DELAY + echo "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds." + set -x + peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt + res=$? + set +x + 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) done - echo - cat log.txt - if test $rc -eq 0; then - echo "===================== Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + echo + cat log.txt + if test $rc -eq 0; then + echo "===================== Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " echo - else - echo "!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!" - echo - exit 1 - fi + else + echo + echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' + echo + exit 1 + fi } chaincodeInvokeInit() { - parsePeerConnectionParameters $@ - res=$? - verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " + parsePeerConnectionParameters $@ + res=$? + verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " - # while 'peer chaincode' command can get the orderer endpoint from the - # peer (if join was successful), let's supply it directly as we know - # it using the "-o" option - set -x - peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n fabcar $PEER_CONN_PARMS --isInit -c '{"function":"initLedger","Args":[]}' >&log.txt - res=$? - set +x - cat log.txt - verifyResult $res "Invoke execution on $PEERS failed " - echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== " - echo + # while 'peer chaincode' command can get the orderer endpoint from the + # peer (if join was successful), let's supply it directly as we know + # it using the "-o" option + set -x + fcn_call='{"function":"'${CC_INIT_FCN}'","Args":[]}' + 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 + cat log.txt + verifyResult $res "Invoke execution on $PEERS failed " + echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== " + echo } chaincodeQuery() { - ORG=$1 - setGlobals $ORG - echo "===================== Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + ORG=$1 + setGlobals $ORG + echo "===================== Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll - # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do - sleep $DELAY - echo "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds." - set -x - peer chaincode query -C $CHANNEL_NAME -n fabcar -c '{"Args":["queryAllCars"]}' >&log.txt - res=$? - set +x + # we either get a successful response, or reach MAX RETRY + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + sleep $DELAY + echo "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds." + set -x + peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt + res=$? + set +x let rc=$res COUNTER=$(expr $COUNTER + 1) done - echo - cat log.txt - if test $rc -eq 0; then - echo "===================== Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + echo + cat log.txt + if test $rc -eq 0; then + echo "===================== Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " echo - else - echo "!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!" - echo - exit 1 - fi + else + echo + echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' + echo + exit 1 + fi } -## at first we package the chaincode +## package the chaincode packageChaincode 1 ## Install chaincode on peer0.org1 and peer0.org2 @@ -284,13 +338,13 @@ commitChaincodeDefinition 1 2 queryCommitted 1 queryCommitted 2 -## Invoke the chaincode -chaincodeInvokeInit 1 2 - -sleep 10 - -# Query chaincode on peer0.org1 -echo "Querying chaincode on peer0.org1..." -chaincodeQuery 1 +## Invoke the chaincode - this does require that the chaincode have the 'initLedger' +## method defined +if [ "$CC_INIT_FCN" = "NA" ]; then + echo "===================== Chaincode initialization is not required ===================== " + echo +else + chaincodeInvokeInit 1 2 +fi exit 0 diff --git a/test-network/scripts/envVar.sh b/test-network/scripts/envVar.sh index 0506914d..6ab0aaa0 100755 --- a/test-network/scripts/envVar.sh +++ b/test-network/scripts/envVar.sh @@ -78,7 +78,7 @@ parsePeerConnectionParameters() { verifyResult() { if [ $1 -ne 0 ]; then - echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo $'\e[1;31m'!!!!!!!!!!!!!!! $2 !!!!!!!!!!!!!!!!$'\e[0m' echo exit 1 fi diff --git a/test-network/scripts/org3-scripts/envVarCLI.sh b/test-network/scripts/org3-scripts/envVarCLI.sh index 01578bc5..5607196e 100644 --- a/test-network/scripts/org3-scripts/envVarCLI.sh +++ b/test-network/scripts/org3-scripts/envVarCLI.sh @@ -47,7 +47,7 @@ setGlobals() { verifyResult() { if [ $1 -ne 0 ]; then - echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo $'\e[1;31m'!!!!!!!!!!!!!!! $2 !!!!!!!!!!!!!!!!$'\e[0m' echo exit 1 fi From c621ca9eb42c9977c53ee0b61f2d9fd3689fdebd Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Wed, 15 Jul 2020 12:44:56 -0400 Subject: [PATCH 03/45] Fix constructQueryResponseFromIterator constructQueryResponseFromIterator was returning a QueryResult slice as opposed to an Asset slice. Signed-off-by: Brett Logan --- .../asset_transfer_ledger_chaincode.go | 43 +++++++++---------- .../chaincode-go/go.mod | 1 + .../chaincode-go/go.sum | 8 ++++ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go index c413429c..2598501b 100644 --- a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go +++ b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go @@ -204,24 +204,23 @@ func (t *SimpleChaincode) TransferAsset(ctx contractapi.TransactionContextInterf return ctx.GetStub().PutState(assetID, assetBytes) } -// constructQueryResponseFromIterator constructs a slice of query results from the resultsIterator -func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) ([]*QueryResult, error) { - var results []*QueryResult +// constructQueryResponseFromIterator constructs a slice of assets from the resultsIterator +func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) ([]*Asset, error) { + var assets []*Asset for resultsIterator.HasNext() { - queryResponse, err := resultsIterator.Next() + queryResult, err := resultsIterator.Next() if err != nil { return nil, err } - - var result *QueryResult - err = json.Unmarshal(queryResponse.Value, &result) + var asset *Asset + err = json.Unmarshal(queryResult.Value, &asset) if err != nil { return nil, err } - results = append(results, result) + assets = append(assets, asset) } - return results, nil + return assets, nil } // GetAssetsByRange performs a range query based on the start and end keys provided. @@ -232,7 +231,7 @@ func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorI // 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. -func (t *SimpleChaincode) GetAssetsByRange(ctx contractapi.TransactionContextInterface, startKey, endKey string) ([]*QueryResult, error) { +func (t *SimpleChaincode) GetAssetsByRange(ctx contractapi.TransactionContextInterface, startKey, endKey string) ([]*Asset, error) { resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey) if err != nil { return nil, err @@ -294,7 +293,7 @@ func (t *SimpleChaincode) TransferAssetByColor(ctx contractapi.TransactionContex // and accepting a single query parameter (owner). // Only available on state databases that support rich query (e.g. CouchDB) // Example: Parameterized rich query -func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]*QueryResult, error) { +func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]*Asset, error) { queryString := fmt.Sprintf(`{"selector":{"docType":"asset","owner":"%s"}}`, owner) return getQueryResultForQueryString(ctx, queryString) } @@ -305,13 +304,13 @@ func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextI // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries. // Only available on state databases that support rich query (e.g. CouchDB) // Example: Ad hoc rich query -func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*QueryResult, error) { +func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) { return getQueryResultForQueryString(ctx, queryString) } // getQueryResultForQueryString executes the passed in query string. // The result set is built and returned as a byte array containing the JSON results. -func getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*QueryResult, error) { +func getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) { resultsIterator, err := ctx.GetStub().GetQueryResult(queryString) if err != nil { return nil, err @@ -331,7 +330,7 @@ func (t *SimpleChaincode) GetAssetsByRangeWithPagination( startKey, endKey, bookmark string, - pageSize int) ([]*QueryResult, error) { + pageSize int) ([]*Asset, error) { resultsIterator, _, err := ctx.GetStub().GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark) if err != nil { @@ -354,7 +353,7 @@ func (t *SimpleChaincode) QueryAssetsWithPagination( ctx contractapi.TransactionContextInterface, queryString, bookmark string, - pageSize int) ([]*QueryResult, error) { + pageSize int) ([]*Asset, error) { return getQueryResultForQueryStringWithPagination(ctx, queryString, int32(pageSize), bookmark) } @@ -365,7 +364,7 @@ func getQueryResultForQueryStringWithPagination( ctx contractapi.TransactionContextInterface, queryString string, pageSize int32, - bookmark string) ([]*QueryResult, error) { + bookmark string) ([]*Asset, error) { resultsIterator, _, err := ctx.GetStub().GetQueryResultWithPagination(queryString, pageSize, bookmark) if err != nil { @@ -425,12 +424,12 @@ func (t *SimpleChaincode) AssetExists(ctx contractapi.TransactionContextInterfac // InitLedger creates the initial set of assets in the ledger. func (t *SimpleChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error { assets := []Asset{ - {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, - {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, - {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, - {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, - {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, - {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, + {DocType: "asset", ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, + {DocType: "asset", ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, + {DocType: "asset", ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, + {DocType: "asset", ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, + {DocType: "asset", ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, + {DocType: "asset", ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, } for _, asset := range assets { diff --git a/asset-transfer-ledger-queries/chaincode-go/go.mod b/asset-transfer-ledger-queries/chaincode-go/go.mod index 6503d770..fc82521a 100644 --- a/asset-transfer-ledger-queries/chaincode-go/go.mod +++ b/asset-transfer-ledger-queries/chaincode-go/go.mod @@ -3,6 +3,7 @@ module github.com/hyperledger/fabric-samples/asset-transfer-ledger-queries/chain go 1.14 require ( + github.com/golang/protobuf v1.3.2 github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a github.com/hyperledger/fabric-contract-api-go v1.1.0 ) diff --git a/asset-transfer-ledger-queries/chaincode-go/go.sum b/asset-transfer-ledger-queries/chaincode-go/go.sum index 201afc0e..1c4ebdfb 100644 --- a/asset-transfer-ledger-queries/chaincode-go/go.sum +++ b/asset-transfer-ledger-queries/chaincode-go/go.sum @@ -13,6 +13,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= @@ -34,6 +35,7 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -56,9 +58,11 @@ github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0L github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -67,6 +71,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= @@ -85,6 +90,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= @@ -113,6 +119,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -130,6 +137,7 @@ google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 14e9ccf85b4bd8658581aeffe9a3d2d54e2cc131 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Wed, 15 Jul 2020 12:50:02 -0400 Subject: [PATCH 04/45] Remove QueryResult Type There was no need for GetAllAssets to return a custom type, it can simple return an array of all assets. Signed-off-by: Brett Logan --- .../chaincode-go/chaincode/smartcontract.go | 16 ++++------------ .../chaincode-go/chaincode/smartcontract_test.go | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go index a2b9b169..8b48798c 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go @@ -21,12 +21,6 @@ type Asset struct { AppraisedValue int `json:"appraisedValue"` } -// QueryResult structure used for handling result of query -type QueryResult struct { - Key string `json:"Key"` - Record *Asset -} - // InitLedger adds a base set of assets to the ledger func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { assets := []Asset{ @@ -163,7 +157,7 @@ func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterfac } // GetAllAssets returns all assets found in world state -func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) { +func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) { // range query with empty string for startKey and endKey does an // open-ended query of all assets in the chaincode namespace. resultsIterator, err := ctx.GetStub().GetStateByRange("", "") @@ -172,7 +166,7 @@ func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface } defer resultsIterator.Close() - var results []QueryResult + var assets []*Asset for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { @@ -184,10 +178,8 @@ func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface if err != nil { return nil, err } - - queryResult := QueryResult{Key: queryResponse.Key, Record: asset} - results = append(results, queryResult) + assets = append(assets, asset) } - return results, nil + return assets, nil } diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go index ef6e2921..cb001ded 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go @@ -169,7 +169,7 @@ func TestGetAllAssets(t *testing.T) { assetTransfer := &chaincode.SmartContract{} assets, err := assetTransfer.GetAllAssets(transactionContext) require.NoError(t, err) - require.Equal(t, []chaincode.QueryResult{{Record: asset}}, assets) + require.Equal(t, []*chaincode.Asset{asset}, assets) iterator.HasNextReturns(true) iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item")) From b4ed9e5afdbe6241f6a738f1e258a8d759a23c91 Mon Sep 17 00:00:00 2001 From: Sijo Cherian Date: Wed, 15 Jul 2020 15:21:38 -0400 Subject: [PATCH 05/45] Simplify asset-transfer-basic javascript application (#241) * Improve asset-transfer-basic javascript app code by combining invoke & query.js Adding a sample application workflow to demonstrate the chaincodes, similar to the WIP PR for basic go-application (https://github.com/hyperledger/fabric-samples/pull/211). Simplify and document-in-code the app workflow of this sample, in a single app , instead of invoke & query setting up Gateway Signed-off-by: Sijo Cherian improve javascript app code by combining invoke & query.js Signed-off-by: Sijo Cherian reverted chaincode func signature Signed-off-by: Sijo Cherian * create/updateAsset signature in sync with chaincode func change as in #227 Signed-off-by: Sijo Cherian * Tested with latest chaincode, Improved comments Signed-off-by: Sijo Cherian Co-authored-by: Sijo Cherian --- .../application-javascript/app.js | 131 ++++++++++++++++++ .../application-javascript/enrollAdmin.js | 24 ++-- .../application-javascript/invoke.js | 63 --------- .../application-javascript/query.js | 64 --------- .../application-javascript/registerUser.js | 36 ++--- 5 files changed, 164 insertions(+), 154 deletions(-) create mode 100644 asset-transfer-basic/application-javascript/app.js delete mode 100644 asset-transfer-basic/application-javascript/invoke.js delete mode 100644 asset-transfer-basic/application-javascript/query.js diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js new file mode 100644 index 00000000..d6eeaa83 --- /dev/null +++ b/asset-transfer-basic/application-javascript/app.js @@ -0,0 +1,131 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const { Gateway, Wallets } = require('fabric-network'); +const path = require('path'); +const fs = require('fs'); +const registerUser = require('./registerUser'); +const enrollAdmin = require('./enrollAdmin'); + +const myChannel = 'mychannel'; +const myChaincodeName = 'basic'; + +function prettyJSONString(inputString) { + return JSON.stringify(JSON.parse(inputString),null,2); +} + +// pre-requisites: +// fabric-sample test-network setup with two peers and an ordering service, +// the companion chaincode is deployed, approved and committed on the channel mychannel +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(__dirname, 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + + // Steps: + // Note: Steps 1 & 2 need to done only once in an app-server per blockchain network + // 1. register & enroll admin user with CA, stores admin identity in local wallet + enrollAdmin.EnrollAdminUser(); + + // 2. register & enroll application user with CA, which is used as client identify to make chaincode calls, stores app user identity in local wallet + registerUser.RegisterAppUser(); + + // Check to see if app user exist in wallet. + const identity = await wallet.get(registerUser.ApplicationUserId); + if (!identity) { + console.log('An identity for the user does not exist in the wallet: '+ registerUser.ApplicationUserId); + return; + } + + //3. Prepare to call chaincode using fabric javascript node sdk + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: registerUser.ApplicationUserId, discovery: { enabled: true, asLocalhost: true } }); + try { + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork(myChannel); + + // Get the contract from the network. + const contract = network.getContract(myChaincodeName); + + //4. Init a set of asset data on the channel using chaincode 'InitLedger' + console.log('Submit Transaction: InitLedger creates the initial set of assets on the ledger.'); + await contract.submitTransaction('InitLedger'); + + //5. *** Some example transactions are listed below *** + + // GetAllAssets returns all the current assets on the ledger + let result = await contract.evaluateTransaction('GetAllAssets'); + console.log('Evaluate Transaction: GetAllAssets, result: ' + prettyJSONString(result.toString()) ); + + console.log('\n***********************'); + console.log('Submit Transaction: CreateAsset asset13'); + //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraizedValue of 1300 + await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', 5, 'Tom', 1300); + + console.log('Evaluate Transaction: ReadAsset asset13'); + // ReadAsset returns an asset with given assetID + result = await contract.evaluateTransaction('ReadAsset', 'asset13'); + console.log(' result: ' + prettyJSONString(result.toString()) ); + + console.log('\n***********************'); + console.log('Evaluate Transaction: AssetExists asset1'); + // AssetExists returns 'true' if an asset with given assetID exist + result = await contract.evaluateTransaction('AssetExists', 'asset1'); + console.log(' result: ' + prettyJSONString(result.toString()) ); + + console.log('Submit Transaction: UpdateAsset asset1, new AppraisedValue : 350'); + // UpdateAsset updates an existing asset with new properties. Same args as CreateAsset + await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', 5, 'Tomoko', 350); + + console.log('Evaluate Transaction: ReadAsset asset1'); + result = await contract.evaluateTransaction('ReadAsset', 'asset1'); + console.log(' result: ' + prettyJSONString(result.toString()) ); + + try { + console.log('\nSubmit Transaction: UpdateAsset asset70'); + //Non existing asset asset70 should throw Error + await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', 5, 'Tomoko', 300); + } + catch (error) { + let errMsg = 'Expected an error on UpdateAsset of non-existing Asset. '; + console.log(errMsg + error); + } + console.log('\n***********************'); + + console.log('Submit Transaction: TransferAsset asset1 from owner Tomoko > owner Tom'); + // TransferAsset transfers an asset with given ID to new owner Tom + await contract.submitTransaction('TransferAsset', 'asset1', 'Tom'); + + console.log('Evaluate Transaction: ReadAsset asset1'); + result = await contract.evaluateTransaction('ReadAsset', 'asset1'); + console.log(' result: ' + prettyJSONString(result.toString()) ); + + } finally { + // Disconnect from the gateway peer when all work for this client identity is complete + gateway.disconnect(); + } + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + + +main(); diff --git a/asset-transfer-basic/application-javascript/enrollAdmin.js b/asset-transfer-basic/application-javascript/enrollAdmin.js index 85d36a0b..79a794e6 100644 --- a/asset-transfer-basic/application-javascript/enrollAdmin.js +++ b/asset-transfer-basic/application-javascript/enrollAdmin.js @@ -10,8 +10,11 @@ const FabricCAServices = require('fabric-ca-client'); const { Wallets } = require('fabric-network'); const fs = require('fs'); const path = require('path'); +const adminUserId = 'admin'; +const adminUserPasswd = 'adminpw'; +const walletPath = path.join(__dirname, 'wallet'); -async function main() { +async function enrollAdminUser() { try { // load the network configuration const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); @@ -26,20 +29,18 @@ async function main() { const caTLSCACerts = caInfo.tlsCACerts.pem; const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); - // Create a new file system based wallet for managing identities. - const walletPath = path.join(__dirname, 'wallet'); + // Create a new wallet : Note that wallet can be resfor managing identities. const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the admin user. - const identity = await wallet.get('admin'); + const identity = await wallet.get(adminUserId); if (identity) { - console.log('An identity for the admin user "admin" already exists in the wallet'); + console.log('An identity for the admin user already exists in the wallet'); return; } // Enroll the admin user, and import the new identity into the wallet. - const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const enrollment = await ca.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd }); const x509Identity = { credentials: { certificate: enrollment.certificate, @@ -48,13 +49,14 @@ async function main() { mspId: 'Org1MSP', type: 'X.509', }; - await wallet.put('admin', x509Identity); - console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + await wallet.put(adminUserId, x509Identity); + console.log('Successfully enrolled admin user and imported it into the wallet'); } catch (error) { - console.error(`Failed to enroll admin user "admin": ${error}`); + console.error(`Failed to enroll admin user : ${error}`); process.exit(1); } } -main(); +exports.AdminUserId = adminUserId; +exports.EnrollAdminUser = enrollAdminUser; diff --git a/asset-transfer-basic/application-javascript/invoke.js b/asset-transfer-basic/application-javascript/invoke.js deleted file mode 100644 index 1092e230..00000000 --- a/asset-transfer-basic/application-javascript/invoke.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright IBM Corp. All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const { Gateway, Wallets } = require('fabric-network'); -const fs = require('fs'); -const path = require('path'); - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(__dirname, 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.js application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Submit the specified transaction. (several example transactions are listed below) - // createAsset creates an asset with ID asset1, color yellow, owner Dave, size 5 and appraizedValue of 130 requires 6 arguments. - // ex: ('createAsset', 'asset1', 'yellow', 'Dave', 5, 1300) - // transferAsset transfers an asset with ID asset1 to new owner Tom - requires 2 arguments. - // ex: ('transferAsset', 'asset1', 'Tom') - await contract.submitTransaction('createAsset', 'asset13', 'yellow', 5, 'Tom', 1300); - console.log('Transaction has been submitted'); - - // Disconnect from the gateway. - await gateway.disconnect(); - - } catch (error) { - console.error(`Failed to submit transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-javascript/query.js b/asset-transfer-basic/application-javascript/query.js deleted file mode 100644 index 9c9adff0..00000000 --- a/asset-transfer-basic/application-javascript/query.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright IBM Corp. All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const { Gateway, Wallets } = require('fabric-network'); -const path = require('path'); -const fs = require('fs'); - - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(__dirname, 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.js application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Evaluate the specified transaction. (several example transactions are listed below) - // getAsset returns an asset with given assetID asset4 - requires 1 argument. - // ex: ('getAsset', 'asset4') - // getAllAssets returns all assets in the world state - requires no arguments. - // ex: ('getAllAssets') - const result = await contract.evaluateTransaction('getAllAssets'); - console.log(`Transaction has been evaluated, result is: ${result.toString()}`); - - // Disconnect from the gateway. - await gateway.disconnect(); - - } catch (error) { - console.error(`Failed to evaluate transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-javascript/registerUser.js b/asset-transfer-basic/application-javascript/registerUser.js index 8320b1bd..16c14535 100644 --- a/asset-transfer-basic/application-javascript/registerUser.js +++ b/asset-transfer-basic/application-javascript/registerUser.js @@ -10,8 +10,12 @@ const { Wallets } = require('fabric-network'); const FabricCAServices = require('fabric-ca-client'); const fs = require('fs'); const path = require('path'); +const enrollAdmin = require('./enrollAdmin'); +const caChaincodeUserRole = 'client'; +const applicationUserId = 'appUser'; +const walletPath = path.join(__dirname, 'wallet'); -async function main() { +async function registerAppUser() { try { // load the network configuration const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); @@ -25,38 +29,37 @@ async function main() { const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; const ca = new FabricCAServices(caURL); - // Create a new file system based wallet for managing identities. - const walletPath = path.join(__dirname, 'wallet'); + // Create a new file system based wallet for managing identities. ; const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); // Check to see if we've already enrolled the user. - const userIdentity = await wallet.get('appUser'); + const userIdentity = await wallet.get(applicationUserId); if (userIdentity) { - console.log('An identity for the user "appUser" already exists in the wallet'); + console.log('An identity for the user '+applicationUserId+' already exists in the wallet'); return; } // Check to see if we've already enrolled the admin user. - const adminIdentity = await wallet.get('admin'); + const adminIdentity = await wallet.get(enrollAdmin.AdminUserId); if (!adminIdentity) { - console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('An identity for the admin user does not exist in the wallet'); console.log('Run the enrollAdmin.js application before retrying'); return; } // build a user object for authenticating with the CA const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); - const adminUser = await provider.getUserContext(adminIdentity, 'admin'); + const adminUser = await provider.getUserContext(adminIdentity, enrollAdmin.AdminUserId); // Register the user, enroll the user, and import the new identity into the wallet. + // if affiliation is specified by client, the affiliation value must be configured in CA const secret = await ca.register({ affiliation: 'org1.department1', - enrollmentID: 'appUser', - role: 'client' + enrollmentID: applicationUserId, + role: caChaincodeUserRole }, adminUser); const enrollment = await ca.enroll({ - enrollmentID: 'appUser', + enrollmentID: applicationUserId, enrollmentSecret: secret }); const x509Identity = { @@ -67,13 +70,14 @@ async function main() { mspId: 'Org1MSP', type: 'X.509', }; - await wallet.put('appUser', x509Identity); - console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); + await wallet.put(applicationUserId, x509Identity); + console.log('Successfully registered and enrolled user '+applicationUserId +' and imported it into the wallet'); } catch (error) { - console.error(`Failed to register user "appUser": ${error}`); + console.error(`Failed to register user : ${error}`); process.exit(1); } } -main(); +exports.ApplicationUserId = applicationUserId; +exports.RegisterAppUser = registerAppUser; From f4c439ce8a646368b76ab760d188815ae256f4e6 Mon Sep 17 00:00:00 2001 From: nikhil550 Date: Fri, 17 Jul 2020 08:58:19 -0400 Subject: [PATCH 06/45] Add endorsement policy to chaincode deployment script (#247) Signed-off-by: NIKHIL E GUPTA Co-authored-by: NIKHIL E GUPTA --- test-network/network.sh | 29 ++++++++++++++++---- test-network/scripts/deployCC.sh | 46 +++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/test-network/network.sh b/test-network/network.sh index 411a0675..6a3eb33b 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -29,20 +29,26 @@ function printHelp() { echo " "$'\e[0;32m'restart$'\e[0m' - restart the network echo echo " Flags:" + echo " Used with "$'\e[0;32m'network.sh up$'\e[0m', $'\e[0;32m'network.sh createChannel$'\e[0m': echo " -ca - create Certificate Authorities to generate the crypto material" echo " -c - channel name to use (defaults to \"mychannel\")" echo " -s - the database backend to use: goleveldb (default) or couchdb" echo " -r - CLI times out after certain number of attempts (defaults to 5)" echo " -d - delay duration in seconds (defaults to 3)" + echo " -i - the tag to be used to launch the network (defaults to \"latest\")" + echo " -cai - the image tag to be used for CA (defaults to \"${CA_IMAGETAG}\")" + echo " -verbose - verbose mode" + echo " Used with "$'\e[0;32m'network.sh deployCC$'\e[0m' + echo " -c - deploy chaincode to channel" echo " -ccn - the short name of the chaincode to deploy: basic (default),ledger, private, secured" echo " -ccl - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" echo " -ccv - chaincode version. 1.0 (default)" echo " -ccs - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc" - echo " -ccp - Optional, chaincode path. Path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." + echo " -ccp - Optional, path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." + echo " -ccep - Optional, chaincode endorsement policy, using signature policy syntax. The default policy requires an endorsement from Org1 and Org2" + echo " -cccg - Optional, path to a private data collections configuration file" echo " -cci - Optional, chaincode init required function to invoke. When provided this function will be invoked after deployment of the chaincode and will define the chaincode as initialization required." - echo " -i - the tag to be used to launch the network (defaults to \"latest\")" - echo " -cai - the image tag to be used for CA (defaults to \"${CA_IMAGETAG}\")" - echo " -verbose - verbose mode" + echo echo " -h - print this message" echo echo " Possible Mode and flag combinations" @@ -383,10 +389,11 @@ function createChannel() { } + ## Call the script to isntall and instantiate a chaincode on the channel function deployCC() { - scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CLI_DELAY $MAX_RETRY $VERBOSE + scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CC_END_POLICY $CC_COLL_CONFIG $CLI_DELAY $MAX_RETRY $VERBOSE if [ $? -ne 0 ]; then echo "ERROR !!! Deploying chaincode failed" @@ -440,6 +447,10 @@ CHANNEL_NAME="mychannel" CC_NAME="basic" # chaincode path defaults to "NA" CC_SRC_PATH="NA" +# endorsement policy defaults to "NA". This would allow chaincodes to use the majority default policy. +CC_END_POLICY="NA" +# collection configuration defaults to "NA" +CC_COLL_CONFIG="NA" # chaincode init function defaults to "NA" CC_INIT_FCN="NA" # use this as the default docker-compose yaml definition @@ -534,6 +545,14 @@ while [[ $# -ge 1 ]] ; do CC_SRC_PATH="$2" shift ;; + -ccep ) + CC_END_POLICY="$2" + shift + ;; + -cccg ) + CC_COLL_CONFIG="$2" + shift + ;; -cci ) CC_INIT_FCN="$2" shift diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index e94afd66..daa434c6 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -5,9 +5,11 @@ CC_SRC_LANGUAGE=${4:-"go"} CC_VERSION=${5:-"1.0"} CC_SEQUENCE=${6:-"1"} CC_INIT_FCN=${7:-"NA"} -DELAY=${8:-"3"} -MAX_RETRY=${9:-"5"} -VERBOSE=${10:-"false"} +CC_END_POLICY=${8:-"NA"} +CC_COLL_CONFIG=${9:-"NA"} +DELAY=${10:-"3"} +MAX_RETRY=${11:-"5"} +VERBOSE=${12:-"false"} echo --- executing with the following echo - CHANNEL_NAME:$'\e[0;32m'$CHANNEL_NAME$'\e[0m' @@ -16,6 +18,8 @@ echo - CC_SRC_PATH:$'\e[0;32m'$CC_SRC_PATH$'\e[0m' echo - CC_SRC_LANGUAGE:$'\e[0;32m'$CC_SRC_LANGUAGE$'\e[0m' echo - CC_VERSION:$'\e[0;32m'$CC_VERSION$'\e[0m' echo - CC_SEQUENCE:$'\e[0;32m'$CC_SEQUENCE$'\e[0m' +echo - CC_END_POLICY:$'\e[0;32m'$CC_END_POLICY$'\e[0m' +echo - CC_COLL_CONFIG:$'\e[0;32m'$CC_COLL_CONFIG$'\e[0m' echo - CC_INIT_FCN:$'\e[0;32m'$CC_INIT_FCN$'\e[0m' echo - DELAY:$'\e[0;32m'$DELAY$'\e[0m' echo - MAX_RETRY:$'\e[0;32m'$MAX_RETRY$'\e[0m' @@ -25,6 +29,8 @@ CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` FABRIC_CFG_PATH=$PWD/../config/ + + # User has not provided a path, therefore the CC_NAME must # be the short name of a known chaincode sample if [ "$CC_SRC_PATH" = "NA" ]; then @@ -59,6 +65,17 @@ if [ "$CC_SRC_PATH" = "NA" ]; then elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-typescript/" fi + + # check that the language is available for the sample chaincode + if [ ! -d "$CC_SRC_PATH" ]; then + echo The smart contract language "$CC_SRC_LANGUAGE" is not yet available for + echo the "$CC_NAME" sample smart contract + exit 1 + fi +## Make sure that the path the chaincode exists if provided +elif [ ! -d "$CC_SRC_PATH" ]; then + echo Path to chaincode does not exist. Please provide different path + exit 1 fi # do some language specific preparation to the chaincode before packaging @@ -106,6 +123,23 @@ if [ "$CC_INIT_FCN" = "NA" ]; then INIT_REQUIRED="" fi +if [ "$CC_END_POLICY" = "NA" ]; then + CC_END_POLICY="" +else + CC_END_POLICY="--signature-policy $CC_END_POLICY" +fi + +if [ "$CC_COLL_CONFIG" = "NA" ]; then + CC_COLL_CONFIG="" +else + CC_COLL_CONFIG="--collections-config $CC_COLL_CONFIG" +fi + + +#if [ "$CC_INIT_FCN" = "NA" ]; then +# INIT_REQUIRED="" +#fi + # import utils . scripts/envVar.sh @@ -157,7 +191,7 @@ approveForMyOrg() { ORG=$1 setGlobals $ORG 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} >&log.txt + 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 set +x cat log.txt verifyResult $res "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" @@ -179,7 +213,7 @@ checkCommitReadiness() { sleep $DELAY echo "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." set -x - peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} --output json >&log.txt + 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 let rc=0 @@ -209,7 +243,7 @@ commitChaincodeDefinition() { # peer (if join was successful), let's supply it directly as we know # it using the "-o" option 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} >&log.txt + 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 cat log.txt From 12b07b40a9992a7d0046ebbba4df1b639dea7964 Mon Sep 17 00:00:00 2001 From: Naser Mirzaei Date: Mon, 20 Jul 2020 20:07:16 +0430 Subject: [PATCH 07/45] use mkdir -p for create directories (#248) Signed-off-by: Naser Mirzaei --- .../organizations/fabric-ca/registerEnroll.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test-network/organizations/fabric-ca/registerEnroll.sh b/test-network/organizations/fabric-ca/registerEnroll.sh index 22ba1971..b5bf0828 100755 --- a/test-network/organizations/fabric-ca/registerEnroll.sh +++ b/test-network/organizations/fabric-ca/registerEnroll.sh @@ -75,13 +75,13 @@ function createOrg1 { cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/keystore/* ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key - mkdir ${PWD}/organizations/peerOrganizations/org1.example.com/msp/tlscacerts + mkdir -p ${PWD}/organizations/peerOrganizations/org1.example.com/msp/tlscacerts cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/msp/tlscacerts/ca.crt - mkdir ${PWD}/organizations/peerOrganizations/org1.example.com/tlsca + mkdir -p ${PWD}/organizations/peerOrganizations/org1.example.com/tlsca cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem - mkdir ${PWD}/organizations/peerOrganizations/org1.example.com/ca + mkdir -p ${PWD}/organizations/peerOrganizations/org1.example.com/ca cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/cacerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem mkdir -p organizations/peerOrganizations/org1.example.com/users @@ -185,13 +185,13 @@ function createOrg2 { cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/keystore/* ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.key - mkdir ${PWD}/organizations/peerOrganizations/org2.example.com/msp/tlscacerts + mkdir -p ${PWD}/organizations/peerOrganizations/org2.example.com/msp/tlscacerts cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/msp/tlscacerts/ca.crt - mkdir ${PWD}/organizations/peerOrganizations/org2.example.com/tlsca + mkdir -p ${PWD}/organizations/peerOrganizations/org2.example.com/tlsca cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/tlscacerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem - mkdir ${PWD}/organizations/peerOrganizations/org2.example.com/ca + mkdir -p ${PWD}/organizations/peerOrganizations/org2.example.com/ca cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/cacerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem mkdir -p organizations/peerOrganizations/org2.example.com/users @@ -289,10 +289,10 @@ function createOrderer { cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/signcerts/* ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/keystore/* ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key - mkdir ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts + mkdir -p ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/tlscacerts/* ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem - mkdir ${PWD}/organizations/ordererOrganizations/example.com/msp/tlscacerts + mkdir -p ${PWD}/organizations/ordererOrganizations/example.com/msp/tlscacerts cp ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/tlscacerts/* ${PWD}/organizations/ordererOrganizations/example.com/msp/tlscacerts/tlsca.example.com-cert.pem mkdir -p organizations/ordererOrganizations/example.com/users From cad6b90383d195bf4bc101fb67e431c1082422af Mon Sep 17 00:00:00 2001 From: Tiffany Harris Date: Mon, 20 Jul 2020 11:19:41 -0400 Subject: [PATCH 08/45] Fix unmarshaling asset in ReadAsset() Signed-off-by: Tiffany Harris --- .../chaincode-go/asset_transfer_queries.go | 2 +- asset-transfer-secured-agreement/chaincode-go/go.mod | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go b/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go index 0c01c058..7bc567fd 100644 --- a/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go +++ b/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go @@ -38,7 +38,7 @@ func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, a } var asset *Asset - err = json.Unmarshal(assetJSON, asset) + err = json.Unmarshal(assetJSON, &asset) if err != nil { return nil, err } diff --git a/asset-transfer-secured-agreement/chaincode-go/go.mod b/asset-transfer-secured-agreement/chaincode-go/go.mod index 1b48ed47..4f36be90 100644 --- a/asset-transfer-secured-agreement/chaincode-go/go.mod +++ b/asset-transfer-secured-agreement/chaincode-go/go.mod @@ -3,6 +3,7 @@ module github.com/hyperledger/fabric-samples/chaincode/tradingMarbles go 1.14 require ( + github.com/golang/protobuf v1.3.2 github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed github.com/hyperledger/fabric-contract-api-go v1.0.0 ) From d5ce443bee34c54ac43a8aeabb8a38dc23f744e3 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Mon, 20 Jul 2020 12:49:16 -0400 Subject: [PATCH 09/45] WIP (#253) * WIP Signed-off-by: Brett Logan * WIP 2 Signed-off-by: Brett Logan --- test-network/addOrg3/addOrg3.sh | 26 +-- .../addOrg3/fabric-ca/registerEnroll.sh | 40 ++-- test-network/network.sh | 103 ++++------ .../organizations/fabric-ca/registerEnroll.sh | 187 ++++++------------ test-network/scripts/createChannel.sh | 83 ++++---- test-network/scripts/deployCC.sh | 123 ++++++------ test-network/scripts/envVar.sh | 6 +- .../scripts/org3-scripts/step1org3.sh | 42 ++-- .../scripts/org3-scripts/step2org3.sh | 16 +- 9 files changed, 245 insertions(+), 381 deletions(-) diff --git a/test-network/addOrg3/addOrg3.sh b/test-network/addOrg3/addOrg3.sh index 62d4b946..ba7b600f 100755 --- a/test-network/addOrg3/addOrg3.sh +++ b/test-network/addOrg3/addOrg3.sh @@ -15,6 +15,12 @@ export PATH=${PWD}/../../bin:${PWD}:$PATH export FABRIC_CFG_PATH=${PWD} export VERBOSE=false +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + "${@}" +} + # Print the usage message function printHelp () { echo "Usage: " @@ -70,10 +76,8 @@ function generateOrg3() { echo "############ Create Org1 Identities ######################" echo "##########################################################" - set -x - cryptogen generate --config=org3-crypto.yaml --output="../organizations" + execute cryptogen generate --config=org3-crypto.yaml --output="../organizations" res=$? - set +x if [ $res -ne 0 ]; then echo "Failed to generate certificates..." exit 1 @@ -127,15 +131,13 @@ function generateOrg3Definition() { echo "##########################################################" echo "####### Generating Org3 organization definition #########" echo "##########################################################" - export FABRIC_CFG_PATH=$PWD - set -x - configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json - res=$? - set +x - if [ $res -ne 0 ]; then - echo "Failed to generate Org3 config material..." - exit 1 - fi + export FABRIC_CFG_PATH=$PWD + execute configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json + res=$? + if [ $res -ne 0 ]; then + echo "Failed to generate Org3 config material..." + exit 1 + fi echo } diff --git a/test-network/addOrg3/fabric-ca/registerEnroll.sh b/test-network/addOrg3/fabric-ca/registerEnroll.sh index 86cc1209..988847aa 100644 --- a/test-network/addOrg3/fabric-ca/registerEnroll.sh +++ b/test-network/addOrg3/fabric-ca/registerEnroll.sh @@ -1,7 +1,10 @@ - +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + "${@}" +} function createOrg3 { - echo echo "Enroll the CA admin" echo @@ -11,9 +14,7 @@ function createOrg3 { # rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml # rm -rf $FABRIC_CA_CLIENT_HOME/msp - 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 + execute fabric-ca-client enroll -u https://admin:adminpw@localhost:11054 --caname ca-org3 --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem echo 'NodeOUs: Enable: true @@ -33,23 +34,17 @@ function createOrg3 { echo echo "Register peer0" 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 + execute 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 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 + execute 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 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 + execute 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 mkdir -p ../organizations/peerOrganizations/org3.example.com/peers mkdir -p ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com @@ -57,18 +52,14 @@ function createOrg3 { echo echo "## Generate the peer0 msp" 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 + execute 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 cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp/config.yaml echo echo "## Generate the peer0-tls certificates" 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 + execute 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 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 @@ -90,9 +81,7 @@ function createOrg3 { echo echo "## Generate the user msp" 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 + execute 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 cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/config.yaml @@ -101,10 +90,7 @@ function createOrg3 { echo echo "## Generate the org admin msp" 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 + execute 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 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 6a3eb33b..87def2d6 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -67,6 +67,17 @@ function printHelp() { echo " network.sh deployCC -ccn mychaincode -ccp ./user/mychaincode -ccv 1 -ccl javascript" } +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + echo -e "\033[0;32mOutput\033[0m:" + "${@}" +} + +function info() { + echo -e "\033[0;33mINFO\033[0m: ${1}" +} + # Obtain CONTAINER_IDS and remove them # TODO Might want to make this optional - could clear other containers # This function is called when you bring a network down @@ -189,7 +200,6 @@ function checkPrereqs() { # Create Organziation crypto material using cryptogen or CAs function createOrgs() { - if [ -d "organizations/peerOrganizations" ]; then rm -Rf organizations/peerOrganizations && rm -Rf organizations/ordererOrganizations fi @@ -202,89 +212,63 @@ function createOrgs() { exit 1 fi echo - echo "##########################################################" - echo "##### Generate certificates using cryptogen tool #########" - echo "##########################################################" + info "Generate certificates using cryptogen tool" + info "Create Org1 Identities" + + execute cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" + res=$? + if [ $res -ne 0 ]; then + echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' + exit 1 + fi echo - echo "##########################################################" - echo "############ Create Org1 Identities ######################" - echo "##########################################################" + info "Create Org2 Identities" - set -x - cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" + execute cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" res=$? - set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi + echo - echo "##########################################################" - echo "############ Create Org2 Identities ######################" - echo "##########################################################" + info "Create Orderer Org Identities" - set -x - cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" + execute cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations" res=$? - set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi - - echo "##########################################################" - echo "############ Create Orderer Org Identities ###############" - echo "##########################################################" - - set -x - cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations" - res=$? - set +x - if [ $res -ne 0 ]; then - echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' - exit 1 - fi - + echo fi # Create crypto material using Fabric CAs if [ "$CRYPTO" == "Certificate Authorities" ]; then - echo - echo "##########################################################" - echo "##### Generate certificates using Fabric CA's ############" - echo "##########################################################" - - IMAGE_TAG=${CA_IMAGETAG} docker-compose -f $COMPOSE_FILE_CA up -d 2>&1 - + info "Creating Fabric CA's" + IMAGE_TAG=${CA_IMAGETAG} docker-compose -f "$COMPOSE_FILE_CA" up -d 2>&1 . organizations/fabric-ca/registerEnroll.sh sleep 10 - echo "##########################################################" - echo "############ Create Org1 Identities ######################" - echo "##########################################################" + echo + info "Generate certificates using Fabric CA's" + info "Create Org1 Identities" createOrg1 - echo "##########################################################" - echo "############ Create Org2 Identities ######################" - echo "##########################################################" - + info "Create Org2 Identities" createOrg2 - echo "##########################################################" - echo "############ Create Orderer Org Identities ###############" - echo "##########################################################" - + info "Create Orderer Org Identities" createOrderer - fi - echo - echo "Generate CCP files for Org1 and Org2" + info "Generate CCP files for Org1 and Org2" ./organizations/ccp-generate.sh + echo } # Once you create the organization crypto material, you need to create the @@ -315,25 +299,23 @@ function createOrgs() { # Generate orderer system channel genesis block. function createConsortium() { - which configtxgen if [ "$?" -ne 0 ]; then echo "configtxgen tool not found. exiting" exit 1 fi - echo "######### Generating Orderer Genesis block ##############" + info "Generating Orderer Genesis block" # Note: For some unknown reason (at least for now) the block file can't be # named orderer.genesis.block or the orderer will fail to launch! - set -x - configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block + execute configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block res=$? - set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate orderer genesis block..."$'\e[0m' exit 1 fi + echo } # After we create the org crypto material and the system channel genesis block, @@ -344,7 +326,6 @@ function createConsortium() { # Bring up the peer and orderer nodes using docker compose. function networkUp() { - checkPrereqs # generate artifacts if they don't exist if [ ! -d "organizations/peerOrganizations" ]; then @@ -369,9 +350,7 @@ function networkUp() { ## call the script to join create the channel and join the peers of org1 and org2 function createChannel() { - ## Bring up the network if it is not arleady up. - if [ ! -d "organizations/peerOrganizations" ]; then echo "Bringing up network" networkUp @@ -386,21 +365,17 @@ function createChannel() { echo "Error !!! Create channel failed" exit 1 fi - + echo } ## Call the script to isntall and instantiate a chaincode on the channel function deployCC() { - scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CC_END_POLICY $CC_COLL_CONFIG $CLI_DELAY $MAX_RETRY $VERBOSE - if [ $? -ne 0 ]; then echo "ERROR !!! Deploying chaincode failed" exit 1 fi - - exit 0 } diff --git a/test-network/organizations/fabric-ca/registerEnroll.sh b/test-network/organizations/fabric-ca/registerEnroll.sh index b5bf0828..0671ea2c 100755 --- a/test-network/organizations/fabric-ca/registerEnroll.sh +++ b/test-network/organizations/fabric-ca/registerEnroll.sh @@ -1,19 +1,21 @@ +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + "${@}" +} +function info() { + echo -e "\033[0;33mINFO\033[0m: ${1}" +} function createOrg1 { - - echo - echo "Enroll the CA admin" - echo + info "Enroll the CA admin" mkdir -p organizations/peerOrganizations/org1.example.com/ export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/ -# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml -# rm -rf $FABRIC_CA_CLIENT_HOME/msp - 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 + execute fabric-ca-client enroll -u https://admin:adminpw@localhost:7054 --caname ca-org1 --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem + echo echo 'NodeOUs: Enable: true @@ -30,46 +32,30 @@ function createOrg1 { Certificate: cacerts/localhost-7054-ca-org1.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml + info "Register peer0" + execute 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 echo - echo "Register peer0" - 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 + info "Register user" + execute 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 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 + info "Register the org admin" + execute 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 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 mkdir -p organizations/peerOrganizations/org1.example.com/peers mkdir -p organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com + info "Generate the peer0 msp" + execute 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 echo - echo "## Generate the peer0 msp" - 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/config.yaml + info "Generate the peer0-tls certificates" + execute 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 echo - echo "## Generate the peer0-tls certificates" - 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 - 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt @@ -87,43 +73,30 @@ function createOrg1 { mkdir -p organizations/peerOrganizations/org1.example.com/users mkdir -p organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com + info "Generate the user msp" + execute 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 echo - echo "## Generate the user msp" - 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/config.yaml mkdir -p organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com + info "Generate the org admin msp" + execute 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 echo - echo "## Generate the org admin msp" - 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/config.yaml - } function createOrg2 { - - echo - echo "Enroll the CA admin" - echo + info "Enroll the CA admin" mkdir -p organizations/peerOrganizations/org2.example.com/ export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org2.example.com/ -# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml -# rm -rf $FABRIC_CA_CLIENT_HOME/msp - 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 + execute fabric-ca-client enroll -u https://admin:adminpw@localhost:8054 --caname ca-org2 --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem + echo echo 'NodeOUs: Enable: true @@ -140,46 +113,30 @@ function createOrg2 { Certificate: cacerts/localhost-8054-ca-org2.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml + info "Register peer0" + execute 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 echo - echo "Register peer0" - 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 + info "Register user" + execute 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 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 + info "Register the org admin" + execute 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 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 mkdir -p organizations/peerOrganizations/org2.example.com/peers mkdir -p organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com + info "Generate the peer0 msp" + execute 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 echo - echo "## Generate the peer0 msp" - 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/config.yaml + info "Generate the peer0-tls certificates" + execute 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 echo - echo "## Generate the peer0-tls certificates" - 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 - 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt @@ -197,42 +154,29 @@ function createOrg2 { mkdir -p organizations/peerOrganizations/org2.example.com/users mkdir -p organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com + info "Generate the user msp" + execute 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 echo - echo "## Generate the user msp" - 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/config.yaml mkdir -p organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com + info "Generate the org admin msp" + execute 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 echo - echo "## Generate the org admin msp" - 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/config.yaml - } function createOrderer { - - echo - echo "Enroll the CA admin" - echo + info "Enroll the CA admin" mkdir -p organizations/ordererOrganizations/example.com export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/example.com -# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml -# rm -rf $FABRIC_CA_CLIENT_HOME/msp - 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 + execute fabric-ca-client enroll -u https://admin:adminpw@localhost:9054 --caname ca-orderer --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem + echo echo 'NodeOUs: Enable: true @@ -249,41 +193,27 @@ function createOrderer { Certificate: cacerts/localhost-9054-ca-orderer.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml + info "Register orderer" + execute 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 + echo + info "Register the orderer admin" + execute 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 echo - echo "Register orderer" - 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 - - 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 mkdir -p organizations/ordererOrganizations/example.com/orderers mkdir -p organizations/ordererOrganizations/example.com/orderers/example.com - mkdir -p organizations/ordererOrganizations/example.com/orderers/orderer.example.com + info "Generate the orderer msp" + execute 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 echo - echo "## Generate the orderer msp" - 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 cp ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/config.yaml + info "Generate the orderer-tls certificates" + execute 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 echo - echo "## Generate the orderer-tls certificates" - 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 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 @@ -298,14 +228,9 @@ function createOrderer { mkdir -p organizations/ordererOrganizations/example.com/users mkdir -p organizations/ordererOrganizations/example.com/users/Admin@example.com + info "Generate the admin msp" + execute 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 echo - echo "## Generate the admin msp" - 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 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..a8e6e211 100755 --- a/test-network/scripts/createChannel.sh +++ b/test-network/scripts/createChannel.sh @@ -13,38 +13,41 @@ VERBOSE="$4" # import utils . scripts/envVar.sh +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + echo -e "\033[0;32mOutput\033[0m:" + "${@}" +} + +function info() { + echo -e "\033[0;33mINFO\033[0m: ${1}" +} + if [ ! -d "channel-artifacts" ]; then mkdir channel-artifacts fi createChannelTx() { - - set -x - configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/${CHANNEL_NAME}.tx -channelID $CHANNEL_NAME + execute configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/${CHANNEL_NAME}.tx -channelID $CHANNEL_NAME res=$? - set +x if [ $res -ne 0 ]; then echo "Failed to generate channel configuration transaction..." exit 1 fi echo - } createAncorPeerTx() { - for orgmsp in Org1MSP Org2MSP; do - - echo "####### Generating anchor peer update transaction for ${orgmsp} ##########" - set -x - configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/${orgmsp}anchors.tx -channelID $CHANNEL_NAME -asOrg ${orgmsp} - res=$? - set +x - if [ $res -ne 0 ]; then - echo "Failed to generate anchor peer update transaction for ${orgmsp}..." - exit 1 - fi - echo + info "Generating anchor peer update transaction for ${orgmsp}" + execute configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/${orgmsp}anchors.tx -channelID $CHANNEL_NAME -asOrg ${orgmsp} + res=$? + if [ $res -ne 0 ]; then + echo "Failed to generate anchor peer update transaction for ${orgmsp}..." + exit 1 + fi + echo done } @@ -55,17 +58,14 @@ createChannel() { local COUNTER=1 while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - 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 + execute 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 let rc=$res COUNTER=$(expr $COUNTER + 1) done cat log.txt verifyResult $res "Channel creation failed" - echo - echo "===================== Channel '$CHANNEL_NAME' created ===================== " + info "Channel ${CHANNEL_NAME} created" echo } @@ -76,18 +76,16 @@ joinChannel() { local rc=1 local COUNTER=1 ## Sometimes Join takes time, hence retry - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do + while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - set -x - peer channel join -b ./channel-artifacts/$CHANNEL_NAME.block >&log.txt + execute peer channel join -b ./channel-artifacts/$CHANNEL_NAME.block >&log.txt res=$? - set +x let rc=$res - COUNTER=$(expr $COUNTER + 1) + COUNTER=$((COUNTER + 1)) done cat log.txt - echo verifyResult $res "After $MAX_RETRY attempts, peer0.org${ORG} has failed to join channel '$CHANNEL_NAME' " + echo } updateAnchorPeers() { @@ -98,23 +96,21 @@ updateAnchorPeers() { ## Sometimes Join takes time, hence retry while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - 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 + execute 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 let rc=$res COUNTER=$(expr $COUNTER + 1) done cat log.txt verifyResult $res "Anchor peer update failed" - echo "===================== Anchor peers updated for org '$CORE_PEER_LOCALMSPID' on channel '$CHANNEL_NAME' ===================== " + info "Anchor peers updated for org ${CORE_PEER_LOCALMSPID} on channel ${CHANNEL_NAME}" sleep $DELAY echo } verifyResult() { if [ $1 -ne 0 ]; then - echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo "!!!!!!!!!!!!!!! ${2} !!!!!!!!!!!!!!!!" echo exit 1 fi @@ -123,33 +119,30 @@ verifyResult() { FABRIC_CFG_PATH=${PWD}/configtx ## Create channeltx -echo "### Generating channel create transaction '${CHANNEL_NAME}.tx' ###" +echo +info "Generating channel create transaction ${CHANNEL_NAME}.tx" createChannelTx ## Create anchorpeertx -echo "### Generating anchor peer update transactions ###" +info "Generating anchor peer update transactions" createAncorPeerTx FABRIC_CFG_PATH=$PWD/../config/ ## Create channel -echo "Creating channel "$CHANNEL_NAME +info "Creating channel ${CHANNEL_NAME}" createChannel ## Join all the peers to the channel -echo "Join Org1 peers to the channel..." +info "Join Org1 peers to the channel" joinChannel 1 -echo "Join Org2 peers to the channel..." +info "Join Org2 peers to the channel" joinChannel 2 ## Set the anchor peers for each org in the channel -echo "Updating anchor peers for org1..." +info "Updating anchor peers for org1" updateAnchorPeers 1 -echo "Updating anchor peers for org2..." +info "Updating anchor peers for org2" updateAnchorPeers 2 -echo -echo "========= Channel successfully joined =========== " -echo - -exit 0 +info "Channel successfully joined" diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index daa434c6..eb9cbd92 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -29,12 +29,22 @@ CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` FABRIC_CFG_PATH=$PWD/../config/ +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + echo -e "\033[0;32mOutput\033[0m:" + "${@}" +} +function info() { + echo -e "\033[0;33mINFO\033[0m: ${1}" +} # User has not provided a path, therefore the CC_NAME must # be the short name of a known chaincode sample +echo if [ "$CC_SRC_PATH" = "NA" ]; then - echo Determining the path to the chaincode + info "Determining the path to the chaincode" # first see which chaincode we have. This will be based on the # short name of the known chaincode sample if [ "$CC_NAME" = "basic" ]; then @@ -77,25 +87,26 @@ elif [ ! -d "$CC_SRC_PATH" ]; then echo Path to chaincode does not exist. Please provide different path exit 1 fi +echo # do some language specific preparation to the chaincode before packaging if [ "$CC_SRC_LANGUAGE" = "go" ]; then CC_RUNTIME_LANGUAGE=golang - echo Vendoring Go dependencies at $CC_SRC_PATH + info "Vendoring Go dependencies at ${CC_SRC_PATH}" pushd $CC_SRC_PATH GO111MODULE=on go mod vendor popd - echo Finished vendoring Go dependencies + info "Finished vendoring Go dependencies" elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_RUNTIME_LANGUAGE=java - echo Compiling Java code ... + info "Compiling Java code" pushd $CC_SRC_PATH ./gradlew installDist popd - echo Finished compiling Java code + info "Finished compiling Java code" CC_SRC_PATH=$CC_SRC_PATH/build/install/$CC_NAME elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then @@ -104,12 +115,12 @@ elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then CC_RUNTIME_LANGUAGE=node - echo Compiling TypeScript code into JavaScript ... + info "Compiling TypeScript code into JavaScript" pushd $CC_SRC_PATH npm install npm run build popd - echo Finished compiling TypeScript code into JavaScript + info "Finished compiling TypeScript code into JavaScript" else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script @@ -135,11 +146,6 @@ else CC_COLL_CONFIG="--collections-config $CC_COLL_CONFIG" fi - -#if [ "$CC_INIT_FCN" = "NA" ]; then -# INIT_REQUIRED="" -#fi - # import utils . scripts/envVar.sh @@ -147,13 +153,11 @@ fi packageChaincode() { ORG=$1 setGlobals $ORG - 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 + execute 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 cat log.txt verifyResult $res "Chaincode packaging on peer0.org${ORG} has failed" - echo "===================== Chaincode is packaged on peer0.org${ORG} ===================== " + info " Chaincode is packaged on peer0.org${ORG}" echo } @@ -161,13 +165,11 @@ packageChaincode() { installChaincode() { ORG=$1 setGlobals $ORG - set -x - peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt + execute peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt res=$? - set +x cat log.txt verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" - echo "===================== Chaincode is installed on peer0.org${ORG} ===================== " + info "Chaincode is installed on peer0.org${ORG}" echo } @@ -175,14 +177,12 @@ installChaincode() { queryInstalled() { ORG=$1 setGlobals $ORG - set -x - peer lifecycle chaincode queryinstalled >&log.txt + execute peer lifecycle chaincode queryinstalled >&log.txt res=$? - set +x 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" - echo "===================== Query installed successful on peer0.org${ORG} on channel ===================== " + info "Query installed successful on peer0.org${ORG} on channel" echo } @@ -190,12 +190,10 @@ queryInstalled() { approveForMyOrg() { ORG=$1 setGlobals $ORG - 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 - set +x + execute 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 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' ===================== " + info "Chaincode definition approved on peer0.org${ORG} on channel ${CHANNEL_NAME}" echo } @@ -204,18 +202,16 @@ checkCommitReadiness() { ORG=$1 shift 1 setGlobals $ORG - echo "===================== Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + info "Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel ${CHANNEL_NAME}" local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - echo "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." - 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 + info "Checking the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after ${DELAY} seconds" + execute 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 let rc=0 for var in "$@"; do grep "$var" log.txt &>/dev/null || let rc=1 @@ -224,31 +220,30 @@ checkCommitReadiness() { done cat log.txt if test $rc -eq 0; then - echo "===================== Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + info "Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Check commit readiness result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi + echo } # commitChaincodeDefinition VERSION PEER ORG (PEER ORG)... commitChaincodeDefinition() { - parsePeerConnectionParameters $@ + parsePeerConnectionParameters "$@" res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option - 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 + execute 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 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' ===================== " + info "Chaincode definition committed on channel '${CHANNEL_NAME}" echo } @@ -257,18 +252,16 @@ queryCommitted() { ORG=$1 setGlobals $ORG EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc" - echo "===================== Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + info "Querying chaincode definition on peer0.org${ORG} on channel ${CHANNEL_NAME}" local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - echo "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds." - set -x - peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt + info "Attempting to Query committed status on peer0.org${ORG}, Retry after ${DELAY} seconds." + execute peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt res=$? - set +x 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) @@ -276,74 +269,70 @@ queryCommitted() { echo cat log.txt if test $rc -eq 0; then - echo "===================== Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " - echo + info "Query chaincode definition successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi + echo } chaincodeInvokeInit() { - parsePeerConnectionParameters $@ + parsePeerConnectionParameters "$@" res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option - set -x fcn_call='{"function":"'${CC_INIT_FCN}'","Args":[]}' - 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 + info "invoke fcn call:${fcn_call}" + execute 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 cat log.txt verifyResult $res "Invoke execution on $PEERS failed " - echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== " + info "Invoke transaction successful on $PEERS on channel ${CHANNEL_NAME}" echo } chaincodeQuery() { ORG=$1 setGlobals $ORG - echo "===================== Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " + info "Querying on peer0.org${ORG} on channel ${CHANNEL_NAME}" local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - echo "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds." - set -x - peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt + info "Attempting to Query peer0.org${ORG}, Retry after ${DELAY} seconds." + execute peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt res=$? - set +x let rc=$res - COUNTER=$(expr $COUNTER + 1) + COUNTER=$((COUNTER + 1)) done echo cat log.txt if test $rc -eq 0; then - echo "===================== Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " - echo + info "Query successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi + echo } ## package the chaincode packageChaincode 1 ## Install chaincode on peer0.org1 and peer0.org2 -echo "Installing chaincode on peer0.org1..." +info "Installing chaincode on peer0.org1" installChaincode 1 -echo "Install chaincode on peer0.org2..." +info "Install chaincode on peer0.org2" installChaincode 2 ## query whether the chaincode is installed @@ -375,10 +364,8 @@ queryCommitted 2 ## Invoke the chaincode - this does require that the chaincode have the 'initLedger' ## method defined if [ "$CC_INIT_FCN" = "NA" ]; then - echo "===================== Chaincode initialization is not required ===================== " + info "Chaincode initialization is not required" echo else chaincodeInvokeInit 1 2 fi - -exit 0 diff --git a/test-network/scripts/envVar.sh b/test-network/scripts/envVar.sh index 6ab0aaa0..77c716bd 100755 --- a/test-network/scripts/envVar.sh +++ b/test-network/scripts/envVar.sh @@ -12,6 +12,10 @@ export PEER0_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/pee export PEER0_ORG2_CA=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt export PEER0_ORG3_CA=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt +function info() { + echo -e "\033[0;33mINFO\033[0m: ${1}" +} + # Set OrdererOrg.Admin globals setOrdererGlobals() { export CORE_PEER_LOCALMSPID="OrdererMSP" @@ -27,7 +31,7 @@ setGlobals() { else USING_ORG="${OVERRIDE_ORG}" fi - echo "Using organization ${USING_ORG}" + info "Using organization ${USING_ORG}" if [ $USING_ORG -eq 1 ]; then export CORE_PEER_LOCALMSPID="Org1MSP" export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA diff --git a/test-network/scripts/org3-scripts/step1org3.sh b/test-network/scripts/org3-scripts/step1org3.sh index 2e0dc0ea..bdb9b0d6 100755 --- a/test-network/scripts/org3-scripts/step1org3.sh +++ b/test-network/scripts/org3-scripts/step1org3.sh @@ -25,6 +25,11 @@ MAX_RETRY=5 # import environment variables . scripts/org3-scripts/envVarCLI.sh +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + "${@}" +} # fetchChannelConfig # Writes the current channel config for a given channel to a JSON file @@ -38,14 +43,10 @@ fetchChannelConfig() { setGlobals $ORG 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 + execute peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL --tls --cafile $ORDERER_CA 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 + execute configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config >"${OUTPUT}" } # createConfigUpdate @@ -57,14 +58,13 @@ createConfigUpdate() { MODIFIED=$3 OUTPUT=$4 - set -x - configtxlator proto_encode --input "${ORIGINAL}" --type common.Config >original_config.pb - configtxlator proto_encode --input "${MODIFIED}" --type common.Config >modified_config.pb - configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb >config_update.pb - 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 + execute configtxlator proto_encode --input "${ORIGINAL}" --type common.Config >original_config.pb + execute configtxlator proto_encode --input "${MODIFIED}" --type common.Config >modified_config.pb + execute configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb >config_update.pb + execute configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate >config_update.json + # TODO + execute echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json + execute configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope >"${OUTPUT}" } # signConfigtxAsPeerOrg @@ -73,9 +73,7 @@ signConfigtxAsPeerOrg() { PEERORG=$1 TX=$2 setGlobals $PEERORG - set -x - peer channel signconfigtx -f "${TX}" - set +x + execute peer channel signconfigtx -f "${TX}" } echo @@ -86,9 +84,7 @@ echo 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 +execute jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json # 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 @@ -105,12 +101,8 @@ echo echo "========= Submitting transaction from a different peer (peer0.org2) which also signs it ========= " 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 +execute 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} echo echo "========= Config transaction to add org3 to network submitted! =========== " echo - -exit 0 diff --git a/test-network/scripts/org3-scripts/step2org3.sh b/test-network/scripts/org3-scripts/step2org3.sh index ade5f3f2..a13bc789 100755 --- a/test-network/scripts/org3-scripts/step2org3.sh +++ b/test-network/scripts/org3-scripts/step2org3.sh @@ -28,15 +28,19 @@ MAX_RETRY=5 # import environment variables . scripts/org3-scripts/envVarCLI.sh +# execute - Prints and executes the command +function execute() { + echo -e "\033[0;32mCommand\033[0m: ${*}" + "${@}" +} + ## Sometimes Join takes time hence RETRY at least 5 times joinChannelWithRetry() { ORG=$1 setGlobals $ORG - set -x - peer channel join -b $CHANNEL_NAME.block >&log.txt + execute peer channel join -b $CHANNEL_NAME.block >&log.txt res=$? - set +x cat log.txt if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then COUNTER=$(expr $COUNTER + 1) @@ -51,10 +55,8 @@ joinChannelWithRetry() { 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 +execute 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 cat log.txt verifyResult $res "Fetching config block from orderer has Failed" @@ -64,5 +66,3 @@ echo "===================== peer0.org3 joined channel '$CHANNEL_NAME' ========== echo echo "========= Finished adding Org3 to your test network! ========= " echo - -exit 0 From daa732d3d8bbe62ecb72b0191d99187eb7f7538a Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Mon, 20 Jul 2020 12:55:39 -0400 Subject: [PATCH 10/45] Revert "WIP (#253)" This reverts commit d5ce443bee34c54ac43a8aeabb8a38dc23f744e3. Signed-off-by: Brett Logan --- test-network/addOrg3/addOrg3.sh | 26 ++- .../addOrg3/fabric-ca/registerEnroll.sh | 40 ++-- test-network/network.sh | 93 +++++---- .../organizations/fabric-ca/registerEnroll.sh | 187 ++++++++++++------ test-network/scripts/createChannel.sh | 83 ++++---- test-network/scripts/deployCC.sh | 123 ++++++------ test-network/scripts/envVar.sh | 6 +- .../scripts/org3-scripts/step1org3.sh | 42 ++-- .../scripts/org3-scripts/step2org3.sh | 16 +- 9 files changed, 376 insertions(+), 240 deletions(-) diff --git a/test-network/addOrg3/addOrg3.sh b/test-network/addOrg3/addOrg3.sh index ba7b600f..62d4b946 100755 --- a/test-network/addOrg3/addOrg3.sh +++ b/test-network/addOrg3/addOrg3.sh @@ -15,12 +15,6 @@ export PATH=${PWD}/../../bin:${PWD}:$PATH export FABRIC_CFG_PATH=${PWD} export VERBOSE=false -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - "${@}" -} - # Print the usage message function printHelp () { echo "Usage: " @@ -76,8 +70,10 @@ function generateOrg3() { echo "############ Create Org1 Identities ######################" echo "##########################################################" - execute cryptogen generate --config=org3-crypto.yaml --output="../organizations" + set -x + cryptogen generate --config=org3-crypto.yaml --output="../organizations" res=$? + set +x if [ $res -ne 0 ]; then echo "Failed to generate certificates..." exit 1 @@ -131,13 +127,15 @@ function generateOrg3Definition() { echo "##########################################################" echo "####### Generating Org3 organization definition #########" echo "##########################################################" - export FABRIC_CFG_PATH=$PWD - execute configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json - res=$? - if [ $res -ne 0 ]; then - echo "Failed to generate Org3 config material..." - exit 1 - fi + export FABRIC_CFG_PATH=$PWD + set -x + configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json + res=$? + set +x + if [ $res -ne 0 ]; then + echo "Failed to generate Org3 config material..." + exit 1 + fi echo } diff --git a/test-network/addOrg3/fabric-ca/registerEnroll.sh b/test-network/addOrg3/fabric-ca/registerEnroll.sh index 988847aa..86cc1209 100644 --- a/test-network/addOrg3/fabric-ca/registerEnroll.sh +++ b/test-network/addOrg3/fabric-ca/registerEnroll.sh @@ -1,10 +1,7 @@ -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - "${@}" -} + function createOrg3 { + echo echo "Enroll the CA admin" echo @@ -14,7 +11,9 @@ function createOrg3 { # rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml # rm -rf $FABRIC_CA_CLIENT_HOME/msp - execute fabric-ca-client enroll -u https://admin:adminpw@localhost:11054 --caname ca-org3 --tls.certfiles ${PWD}/fabric-ca/org3/tls-cert.pem + 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 echo 'NodeOUs: Enable: true @@ -34,17 +33,23 @@ function createOrg3 { echo echo "Register peer0" echo - execute 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 + 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 echo echo "Register user" echo - execute 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 + 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 echo echo "Register the org admin" echo - execute 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 + 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 mkdir -p ../organizations/peerOrganizations/org3.example.com/peers mkdir -p ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com @@ -52,14 +57,18 @@ function createOrg3 { echo echo "## Generate the peer0 msp" echo - execute 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 + 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 cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp/config.yaml echo echo "## Generate the peer0-tls certificates" echo - execute 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 + 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 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 @@ -81,7 +90,9 @@ function createOrg3 { echo echo "## Generate the user msp" echo - execute 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 + 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 cp ${PWD}/../organizations/peerOrganizations/org3.example.com/msp/config.yaml ${PWD}/../organizations/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/config.yaml @@ -90,7 +101,10 @@ function createOrg3 { echo echo "## Generate the org admin msp" echo - execute 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 + 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 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 87def2d6..6a3eb33b 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -67,17 +67,6 @@ function printHelp() { echo " network.sh deployCC -ccn mychaincode -ccp ./user/mychaincode -ccv 1 -ccl javascript" } -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - echo -e "\033[0;32mOutput\033[0m:" - "${@}" -} - -function info() { - echo -e "\033[0;33mINFO\033[0m: ${1}" -} - # Obtain CONTAINER_IDS and remove them # TODO Might want to make this optional - could clear other containers # This function is called when you bring a network down @@ -200,6 +189,7 @@ function checkPrereqs() { # Create Organziation crypto material using cryptogen or CAs function createOrgs() { + if [ -d "organizations/peerOrganizations" ]; then rm -Rf organizations/peerOrganizations && rm -Rf organizations/ordererOrganizations fi @@ -212,63 +202,89 @@ function createOrgs() { exit 1 fi echo - info "Generate certificates using cryptogen tool" - info "Create Org1 Identities" + echo "##########################################################" + echo "##### Generate certificates using cryptogen tool #########" + echo "##########################################################" + echo - execute cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" + echo "##########################################################" + echo "############ Create Org1 Identities ######################" + echo "##########################################################" + + set -x + cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" res=$? + set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi - echo - info "Create Org2 Identities" + echo "##########################################################" + echo "############ Create Org2 Identities ######################" + echo "##########################################################" - execute cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" + set -x + cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" res=$? + set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi - echo - info "Create Orderer Org Identities" + echo "##########################################################" + echo "############ Create Orderer Org Identities ###############" + echo "##########################################################" - execute cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations" + set -x + cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations" res=$? + set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate certificates..."$'\e[0m' exit 1 fi - echo + fi # Create crypto material using Fabric CAs if [ "$CRYPTO" == "Certificate Authorities" ]; then + echo - info "Creating Fabric CA's" - IMAGE_TAG=${CA_IMAGETAG} docker-compose -f "$COMPOSE_FILE_CA" up -d 2>&1 + echo "##########################################################" + echo "##### Generate certificates using Fabric CA's ############" + echo "##########################################################" + + IMAGE_TAG=${CA_IMAGETAG} docker-compose -f $COMPOSE_FILE_CA up -d 2>&1 + . organizations/fabric-ca/registerEnroll.sh sleep 10 - echo - info "Generate certificates using Fabric CA's" + echo "##########################################################" + echo "############ Create Org1 Identities ######################" + echo "##########################################################" - info "Create Org1 Identities" createOrg1 - info "Create Org2 Identities" + echo "##########################################################" + echo "############ Create Org2 Identities ######################" + echo "##########################################################" + createOrg2 - info "Create Orderer Org Identities" + echo "##########################################################" + echo "############ Create Orderer Org Identities ###############" + echo "##########################################################" + createOrderer + fi - info "Generate CCP files for Org1 and Org2" - ./organizations/ccp-generate.sh echo + echo "Generate CCP files for Org1 and Org2" + ./organizations/ccp-generate.sh } # Once you create the organization crypto material, you need to create the @@ -299,23 +315,25 @@ function createOrgs() { # Generate orderer system channel genesis block. function createConsortium() { + which configtxgen if [ "$?" -ne 0 ]; then echo "configtxgen tool not found. exiting" exit 1 fi - info "Generating Orderer Genesis block" + echo "######### Generating Orderer Genesis block ##############" # Note: For some unknown reason (at least for now) the block file can't be # named orderer.genesis.block or the orderer will fail to launch! - execute configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block + set -x + configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block res=$? + set +x if [ $res -ne 0 ]; then echo $'\e[1;32m'"Failed to generate orderer genesis block..."$'\e[0m' exit 1 fi - echo } # After we create the org crypto material and the system channel genesis block, @@ -326,6 +344,7 @@ function createConsortium() { # Bring up the peer and orderer nodes using docker compose. function networkUp() { + checkPrereqs # generate artifacts if they don't exist if [ ! -d "organizations/peerOrganizations" ]; then @@ -350,7 +369,9 @@ function networkUp() { ## call the script to join create the channel and join the peers of org1 and org2 function createChannel() { + ## Bring up the network if it is not arleady up. + if [ ! -d "organizations/peerOrganizations" ]; then echo "Bringing up network" networkUp @@ -365,17 +386,21 @@ function createChannel() { echo "Error !!! Create channel failed" exit 1 fi - echo + } ## Call the script to isntall and instantiate a chaincode on the channel function deployCC() { + scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CC_END_POLICY $CC_COLL_CONFIG $CLI_DELAY $MAX_RETRY $VERBOSE + if [ $? -ne 0 ]; then echo "ERROR !!! Deploying chaincode failed" exit 1 fi + + exit 0 } diff --git a/test-network/organizations/fabric-ca/registerEnroll.sh b/test-network/organizations/fabric-ca/registerEnroll.sh index 0671ea2c..b5bf0828 100755 --- a/test-network/organizations/fabric-ca/registerEnroll.sh +++ b/test-network/organizations/fabric-ca/registerEnroll.sh @@ -1,21 +1,19 @@ -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - "${@}" -} -function info() { - echo -e "\033[0;33mINFO\033[0m: ${1}" -} function createOrg1 { - info "Enroll the CA admin" + + echo + echo "Enroll the CA admin" + echo mkdir -p organizations/peerOrganizations/org1.example.com/ export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/ +# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml +# rm -rf $FABRIC_CA_CLIENT_HOME/msp - execute fabric-ca-client enroll -u https://admin:adminpw@localhost:7054 --caname ca-org1 --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem - echo + 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 echo 'NodeOUs: Enable: true @@ -32,30 +30,46 @@ function createOrg1 { Certificate: cacerts/localhost-7054-ca-org1.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml - info "Register peer0" - execute 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 echo + echo "Register peer0" + 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 - info "Register user" - execute 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 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 - info "Register the org admin" - execute 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 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 mkdir -p organizations/peerOrganizations/org1.example.com/peers mkdir -p organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com - info "Generate the peer0 msp" - execute 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 echo + echo "## Generate the peer0 msp" + 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/config.yaml - info "Generate the peer0-tls certificates" - execute 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 echo + echo "## Generate the peer0-tls certificates" + 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 + 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt @@ -73,30 +87,43 @@ function createOrg1 { mkdir -p organizations/peerOrganizations/org1.example.com/users mkdir -p organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com - info "Generate the user msp" - execute 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 echo + echo "## Generate the user msp" + 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/config.yaml mkdir -p organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com - info "Generate the org admin msp" - execute 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 echo + echo "## Generate the org admin msp" + 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 cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/config.yaml + } function createOrg2 { - info "Enroll the CA admin" + + echo + echo "Enroll the CA admin" + echo mkdir -p organizations/peerOrganizations/org2.example.com/ export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org2.example.com/ +# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml +# rm -rf $FABRIC_CA_CLIENT_HOME/msp - execute fabric-ca-client enroll -u https://admin:adminpw@localhost:8054 --caname ca-org2 --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem - echo + 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 echo 'NodeOUs: Enable: true @@ -113,30 +140,46 @@ function createOrg2 { Certificate: cacerts/localhost-8054-ca-org2.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml - info "Register peer0" - execute 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 echo + echo "Register peer0" + 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 - info "Register user" - execute 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 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 - info "Register the org admin" - execute 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 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 mkdir -p organizations/peerOrganizations/org2.example.com/peers mkdir -p organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com - info "Generate the peer0 msp" - execute 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 echo + echo "## Generate the peer0 msp" + 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/config.yaml - info "Generate the peer0-tls certificates" - execute 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 echo + echo "## Generate the peer0-tls certificates" + 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 + 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/signcerts/* ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt @@ -154,29 +197,42 @@ function createOrg2 { mkdir -p organizations/peerOrganizations/org2.example.com/users mkdir -p organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com - info "Generate the user msp" - execute 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 echo + echo "## Generate the user msp" + 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/config.yaml mkdir -p organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com - info "Generate the org admin msp" - execute 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 echo + echo "## Generate the org admin msp" + 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 cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/config.yaml + } function createOrderer { - info "Enroll the CA admin" + + echo + echo "Enroll the CA admin" + echo mkdir -p organizations/ordererOrganizations/example.com export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/ordererOrganizations/example.com +# rm -rf $FABRIC_CA_CLIENT_HOME/fabric-ca-client-config.yaml +# rm -rf $FABRIC_CA_CLIENT_HOME/msp - execute fabric-ca-client enroll -u https://admin:adminpw@localhost:9054 --caname ca-orderer --tls.certfiles ${PWD}/organizations/fabric-ca/ordererOrg/tls-cert.pem - echo + 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 echo 'NodeOUs: Enable: true @@ -193,27 +249,41 @@ function createOrderer { Certificate: cacerts/localhost-9054-ca-orderer.pem OrganizationalUnitIdentifier: orderer' > ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml - info "Register orderer" - execute 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 - echo - info "Register the orderer admin" - execute 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 echo + echo "Register orderer" + 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 + + 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 mkdir -p organizations/ordererOrganizations/example.com/orderers mkdir -p organizations/ordererOrganizations/example.com/orderers/example.com + mkdir -p organizations/ordererOrganizations/example.com/orderers/orderer.example.com - info "Generate the orderer msp" - execute 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 echo + echo "## Generate the orderer msp" + 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 cp ${PWD}/organizations/ordererOrganizations/example.com/msp/config.yaml ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/config.yaml - info "Generate the orderer-tls certificates" - execute 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 echo + echo "## Generate the orderer-tls certificates" + 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 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 @@ -228,9 +298,14 @@ function createOrderer { mkdir -p organizations/ordererOrganizations/example.com/users mkdir -p organizations/ordererOrganizations/example.com/users/Admin@example.com - info "Generate the admin msp" - execute 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 echo + echo "## Generate the admin msp" + 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 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 a8e6e211..1bf050ae 100755 --- a/test-network/scripts/createChannel.sh +++ b/test-network/scripts/createChannel.sh @@ -13,41 +13,38 @@ VERBOSE="$4" # import utils . scripts/envVar.sh -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - echo -e "\033[0;32mOutput\033[0m:" - "${@}" -} - -function info() { - echo -e "\033[0;33mINFO\033[0m: ${1}" -} - if [ ! -d "channel-artifacts" ]; then mkdir channel-artifacts fi createChannelTx() { - execute configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/${CHANNEL_NAME}.tx -channelID $CHANNEL_NAME + + set -x + configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/${CHANNEL_NAME}.tx -channelID $CHANNEL_NAME res=$? + set +x if [ $res -ne 0 ]; then echo "Failed to generate channel configuration transaction..." exit 1 fi echo + } createAncorPeerTx() { + for orgmsp in Org1MSP Org2MSP; do - info "Generating anchor peer update transaction for ${orgmsp}" - execute configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/${orgmsp}anchors.tx -channelID $CHANNEL_NAME -asOrg ${orgmsp} - res=$? - if [ $res -ne 0 ]; then - echo "Failed to generate anchor peer update transaction for ${orgmsp}..." - exit 1 - fi - echo + + echo "####### Generating anchor peer update transaction for ${orgmsp} ##########" + set -x + configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/${orgmsp}anchors.tx -channelID $CHANNEL_NAME -asOrg ${orgmsp} + res=$? + set +x + if [ $res -ne 0 ]; then + echo "Failed to generate anchor peer update transaction for ${orgmsp}..." + exit 1 + fi + echo done } @@ -58,14 +55,17 @@ createChannel() { local COUNTER=1 while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - execute 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 + 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 let rc=$res COUNTER=$(expr $COUNTER + 1) done cat log.txt verifyResult $res "Channel creation failed" - info "Channel ${CHANNEL_NAME} created" + echo + echo "===================== Channel '$CHANNEL_NAME' created ===================== " echo } @@ -76,16 +76,18 @@ joinChannel() { local rc=1 local COUNTER=1 ## Sometimes Join takes time, hence retry - while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ] ; do + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - execute peer channel join -b ./channel-artifacts/$CHANNEL_NAME.block >&log.txt + set -x + peer channel join -b ./channel-artifacts/$CHANNEL_NAME.block >&log.txt res=$? + set +x let rc=$res - COUNTER=$((COUNTER + 1)) + COUNTER=$(expr $COUNTER + 1) done cat log.txt - verifyResult $res "After $MAX_RETRY attempts, peer0.org${ORG} has failed to join channel '$CHANNEL_NAME' " echo + verifyResult $res "After $MAX_RETRY attempts, peer0.org${ORG} has failed to join channel '$CHANNEL_NAME' " } updateAnchorPeers() { @@ -96,21 +98,23 @@ updateAnchorPeers() { ## Sometimes Join takes time, hence retry while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do sleep $DELAY - execute 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 + 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 let rc=$res COUNTER=$(expr $COUNTER + 1) done cat log.txt verifyResult $res "Anchor peer update failed" - info "Anchor peers updated for org ${CORE_PEER_LOCALMSPID} on channel ${CHANNEL_NAME}" + echo "===================== Anchor peers updated for org '$CORE_PEER_LOCALMSPID' on channel '$CHANNEL_NAME' ===================== " sleep $DELAY echo } verifyResult() { if [ $1 -ne 0 ]; then - echo "!!!!!!!!!!!!!!! ${2} !!!!!!!!!!!!!!!!" + echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" echo exit 1 fi @@ -119,30 +123,33 @@ verifyResult() { FABRIC_CFG_PATH=${PWD}/configtx ## Create channeltx -echo -info "Generating channel create transaction ${CHANNEL_NAME}.tx" +echo "### Generating channel create transaction '${CHANNEL_NAME}.tx' ###" createChannelTx ## Create anchorpeertx -info "Generating anchor peer update transactions" +echo "### Generating anchor peer update transactions ###" createAncorPeerTx FABRIC_CFG_PATH=$PWD/../config/ ## Create channel -info "Creating channel ${CHANNEL_NAME}" +echo "Creating channel "$CHANNEL_NAME createChannel ## Join all the peers to the channel -info "Join Org1 peers to the channel" +echo "Join Org1 peers to the channel..." joinChannel 1 -info "Join Org2 peers to the channel" +echo "Join Org2 peers to the channel..." joinChannel 2 ## Set the anchor peers for each org in the channel -info "Updating anchor peers for org1" +echo "Updating anchor peers for org1..." updateAnchorPeers 1 -info "Updating anchor peers for org2" +echo "Updating anchor peers for org2..." updateAnchorPeers 2 -info "Channel successfully joined" +echo +echo "========= Channel successfully joined =========== " +echo + +exit 0 diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index eb9cbd92..daa434c6 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -29,22 +29,12 @@ CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` FABRIC_CFG_PATH=$PWD/../config/ -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - echo -e "\033[0;32mOutput\033[0m:" - "${@}" -} -function info() { - echo -e "\033[0;33mINFO\033[0m: ${1}" -} # User has not provided a path, therefore the CC_NAME must # be the short name of a known chaincode sample -echo if [ "$CC_SRC_PATH" = "NA" ]; then - info "Determining the path to the chaincode" + echo Determining the path to the chaincode # first see which chaincode we have. This will be based on the # short name of the known chaincode sample if [ "$CC_NAME" = "basic" ]; then @@ -87,26 +77,25 @@ elif [ ! -d "$CC_SRC_PATH" ]; then echo Path to chaincode does not exist. Please provide different path exit 1 fi -echo # do some language specific preparation to the chaincode before packaging if [ "$CC_SRC_LANGUAGE" = "go" ]; then CC_RUNTIME_LANGUAGE=golang - info "Vendoring Go dependencies at ${CC_SRC_PATH}" + echo Vendoring Go dependencies at $CC_SRC_PATH pushd $CC_SRC_PATH GO111MODULE=on go mod vendor popd - info "Finished vendoring Go dependencies" + echo Finished vendoring Go dependencies elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_RUNTIME_LANGUAGE=java - info "Compiling Java code" + echo Compiling Java code ... pushd $CC_SRC_PATH ./gradlew installDist popd - info "Finished compiling Java code" + echo Finished compiling Java code CC_SRC_PATH=$CC_SRC_PATH/build/install/$CC_NAME elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then @@ -115,12 +104,12 @@ elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then CC_RUNTIME_LANGUAGE=node - info "Compiling TypeScript code into JavaScript" + echo Compiling TypeScript code into JavaScript ... pushd $CC_SRC_PATH npm install npm run build popd - info "Finished compiling TypeScript code into JavaScript" + echo Finished compiling TypeScript code into JavaScript else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script @@ -146,6 +135,11 @@ else CC_COLL_CONFIG="--collections-config $CC_COLL_CONFIG" fi + +#if [ "$CC_INIT_FCN" = "NA" ]; then +# INIT_REQUIRED="" +#fi + # import utils . scripts/envVar.sh @@ -153,11 +147,13 @@ fi packageChaincode() { ORG=$1 setGlobals $ORG - execute peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME}_${CC_VERSION} >&log.txt + 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 cat log.txt verifyResult $res "Chaincode packaging on peer0.org${ORG} has failed" - info " Chaincode is packaged on peer0.org${ORG}" + echo "===================== Chaincode is packaged on peer0.org${ORG} ===================== " echo } @@ -165,11 +161,13 @@ packageChaincode() { installChaincode() { ORG=$1 setGlobals $ORG - execute peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt + set -x + peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt res=$? + set +x cat log.txt verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" - info "Chaincode is installed on peer0.org${ORG}" + echo "===================== Chaincode is installed on peer0.org${ORG} ===================== " echo } @@ -177,12 +175,14 @@ installChaincode() { queryInstalled() { ORG=$1 setGlobals $ORG - execute peer lifecycle chaincode queryinstalled >&log.txt + set -x + peer lifecycle chaincode queryinstalled >&log.txt res=$? + set +x 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" - info "Query installed successful on peer0.org${ORG} on channel" + echo "===================== Query installed successful on peer0.org${ORG} on channel ===================== " echo } @@ -190,10 +190,12 @@ queryInstalled() { approveForMyOrg() { ORG=$1 setGlobals $ORG - execute 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 + 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 + set +x cat log.txt verifyResult $res "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" - info "Chaincode definition approved on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " echo } @@ -202,16 +204,18 @@ checkCommitReadiness() { ORG=$1 shift 1 setGlobals $ORG - info "Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - info "Checking the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after ${DELAY} seconds" - execute 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 + echo "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." + 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 let rc=0 for var in "$@"; do grep "$var" log.txt &>/dev/null || let rc=1 @@ -220,30 +224,31 @@ checkCommitReadiness() { done cat log.txt if test $rc -eq 0; then - info "Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Check commit readiness result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi - echo } # commitChaincodeDefinition VERSION PEER ORG (PEER ORG)... commitChaincodeDefinition() { - parsePeerConnectionParameters "$@" + parsePeerConnectionParameters $@ res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option - execute 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 + 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 cat log.txt verifyResult $res "Chaincode definition commit failed on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" - info "Chaincode definition committed on channel '${CHANNEL_NAME}" + echo "===================== Chaincode definition committed on channel '$CHANNEL_NAME' ===================== " echo } @@ -252,16 +257,18 @@ queryCommitted() { ORG=$1 setGlobals $ORG EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc" - info "Querying chaincode definition on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - info "Attempting to Query committed status on peer0.org${ORG}, Retry after ${DELAY} seconds." - execute peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt + echo "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds." + set -x + peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt res=$? + set +x 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) @@ -269,70 +276,74 @@ queryCommitted() { echo cat log.txt if test $rc -eq 0; then - info "Query chaincode definition successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + echo else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi - echo } chaincodeInvokeInit() { - parsePeerConnectionParameters "$@" + parsePeerConnectionParameters $@ res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option + set -x fcn_call='{"function":"'${CC_INIT_FCN}'","Args":[]}' - info "invoke fcn call:${fcn_call}" - execute 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 + 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 cat log.txt verifyResult $res "Invoke execution on $PEERS failed " - info "Invoke transaction successful on $PEERS on channel ${CHANNEL_NAME}" + echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== " echo } chaincodeQuery() { ORG=$1 setGlobals $ORG - info "Querying on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'... ===================== " local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY - while [ $rc -ne 0 ] && [ $COUNTER -lt $MAX_RETRY ]; do + while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY - info "Attempting to Query peer0.org${ORG}, Retry after ${DELAY} seconds." - execute peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt + echo "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds." + set -x + peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt res=$? + set +x let rc=$res - COUNTER=$((COUNTER + 1)) + COUNTER=$(expr $COUNTER + 1) done echo cat log.txt if test $rc -eq 0; then - info "Query successful on peer0.org${ORG} on channel ${CHANNEL_NAME}" + echo "===================== Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME' ===================== " + echo else echo echo $'\e[1;31m'"!!!!!!!!!!!!!!! After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID !!!!!!!!!!!!!!!!"$'\e[0m' echo exit 1 fi - echo } ## package the chaincode packageChaincode 1 ## Install chaincode on peer0.org1 and peer0.org2 -info "Installing chaincode on peer0.org1" +echo "Installing chaincode on peer0.org1..." installChaincode 1 -info "Install chaincode on peer0.org2" +echo "Install chaincode on peer0.org2..." installChaincode 2 ## query whether the chaincode is installed @@ -364,8 +375,10 @@ queryCommitted 2 ## Invoke the chaincode - this does require that the chaincode have the 'initLedger' ## method defined if [ "$CC_INIT_FCN" = "NA" ]; then - info "Chaincode initialization is not required" + echo "===================== Chaincode initialization is not required ===================== " echo else chaincodeInvokeInit 1 2 fi + +exit 0 diff --git a/test-network/scripts/envVar.sh b/test-network/scripts/envVar.sh index 77c716bd..6ab0aaa0 100755 --- a/test-network/scripts/envVar.sh +++ b/test-network/scripts/envVar.sh @@ -12,10 +12,6 @@ export PEER0_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/pee export PEER0_ORG2_CA=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt export PEER0_ORG3_CA=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt -function info() { - echo -e "\033[0;33mINFO\033[0m: ${1}" -} - # Set OrdererOrg.Admin globals setOrdererGlobals() { export CORE_PEER_LOCALMSPID="OrdererMSP" @@ -31,7 +27,7 @@ setGlobals() { else USING_ORG="${OVERRIDE_ORG}" fi - info "Using organization ${USING_ORG}" + echo "Using organization ${USING_ORG}" if [ $USING_ORG -eq 1 ]; then export CORE_PEER_LOCALMSPID="Org1MSP" export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA diff --git a/test-network/scripts/org3-scripts/step1org3.sh b/test-network/scripts/org3-scripts/step1org3.sh index bdb9b0d6..2e0dc0ea 100755 --- a/test-network/scripts/org3-scripts/step1org3.sh +++ b/test-network/scripts/org3-scripts/step1org3.sh @@ -25,11 +25,6 @@ MAX_RETRY=5 # import environment variables . scripts/org3-scripts/envVarCLI.sh -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - "${@}" -} # fetchChannelConfig # Writes the current channel config for a given channel to a JSON file @@ -43,10 +38,14 @@ fetchChannelConfig() { setGlobals $ORG echo "Fetching the most recent configuration block for the channel" - execute peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL --tls --cafile $ORDERER_CA + 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 echo "Decoding config block to JSON and isolating config to ${OUTPUT}" - execute configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config >"${OUTPUT}" + set -x + configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config >"${OUTPUT}" + set +x } # createConfigUpdate @@ -58,13 +57,14 @@ createConfigUpdate() { MODIFIED=$3 OUTPUT=$4 - execute configtxlator proto_encode --input "${ORIGINAL}" --type common.Config >original_config.pb - execute configtxlator proto_encode --input "${MODIFIED}" --type common.Config >modified_config.pb - execute configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb >config_update.pb - execute configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate >config_update.json - # TODO - execute echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json - execute configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope >"${OUTPUT}" + set -x + configtxlator proto_encode --input "${ORIGINAL}" --type common.Config >original_config.pb + configtxlator proto_encode --input "${MODIFIED}" --type common.Config >modified_config.pb + configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb >config_update.pb + 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 } # signConfigtxAsPeerOrg @@ -73,7 +73,9 @@ signConfigtxAsPeerOrg() { PEERORG=$1 TX=$2 setGlobals $PEERORG - execute peer channel signconfigtx -f "${TX}" + set -x + peer channel signconfigtx -f "${TX}" + set +x } echo @@ -84,7 +86,9 @@ echo fetchChannelConfig 1 ${CHANNEL_NAME} config.json # Modify the configuration to append the new org -execute jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json +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 # 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 @@ -101,8 +105,12 @@ echo echo "========= Submitting transaction from a different peer (peer0.org2) which also signs it ========= " echo setGlobals 2 -execute 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 +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 echo echo "========= Config transaction to add org3 to network submitted! =========== " echo + +exit 0 diff --git a/test-network/scripts/org3-scripts/step2org3.sh b/test-network/scripts/org3-scripts/step2org3.sh index a13bc789..ade5f3f2 100755 --- a/test-network/scripts/org3-scripts/step2org3.sh +++ b/test-network/scripts/org3-scripts/step2org3.sh @@ -28,19 +28,15 @@ MAX_RETRY=5 # import environment variables . scripts/org3-scripts/envVarCLI.sh -# execute - Prints and executes the command -function execute() { - echo -e "\033[0;32mCommand\033[0m: ${*}" - "${@}" -} - ## Sometimes Join takes time hence RETRY at least 5 times joinChannelWithRetry() { ORG=$1 setGlobals $ORG - execute peer channel join -b $CHANNEL_NAME.block >&log.txt + set -x + peer channel join -b $CHANNEL_NAME.block >&log.txt res=$? + set +x cat log.txt if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then COUNTER=$(expr $COUNTER + 1) @@ -55,8 +51,10 @@ joinChannelWithRetry() { echo "Fetching channel config block from orderer..." -execute 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 +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 cat log.txt verifyResult $res "Fetching config block from orderer has Failed" @@ -66,3 +64,5 @@ echo "===================== peer0.org3 joined channel '$CHANNEL_NAME' ========== echo echo "========= Finished adding Org3 to your test network! ========= " echo + +exit 0 From 6acc140671ff73361d00ae2080f61c2395d33e8b Mon Sep 17 00:00:00 2001 From: nikhil550 Date: Mon, 20 Jul 2020 18:33:21 -0400 Subject: [PATCH 11/45] Add asset transfer series documentation to Fabric samples README (#251) Signed-off-by: NIKHIL E GUPTA Co-authored-by: NIKHIL E GUPTA --- README.md | 13 +++++++++++++ .../chaincode-go/README.md | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1acc2672..5483167e 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,19 @@ You can use the following table to learn more about each sample, and find the co | [High throughput](high-throughput) | Learn how you can design your smart contracts to process a large volume of transactions. | | | [Chaincode](chaincode) | A set of sample smart contracts used by other samples and the tutorials in the Fabric documentation. | [Fabric tutorials](https://hyperledger-fabric.readthedocs.io/en/master/tutorials.html) | +### Asset transfer smart contract series + +The asset transfer series provides a series of smart contracts and applications that you can use to create and transfer a generic asset using Hyperledger Fabric. However, each sample is built with different smart contract and application APIs in order to demonstrate different Fabric features. The **Basic** sample provides an introduction on how to write smart contracts and how to interact with a Fabric network using the Fabric SDKs. The **Secured agreement** sample demonstrates how to use more advanced capabilities to develop 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. | **Coming soon** | Go, JavaScript, Typescript | JavaScript, Typescript | +| [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates how to deploy an index with your chaincode and issue rich queries when you are using CouchDB as your state database. | **Coming soon** | Go | **Coming soon** | +| [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections and how the private data hash can be used to verify an agreement before executing a transfer | **Coming soon** | Go | **Coming soon** | +| [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | **Coming soon** | Go | **Coming soon** | + +The asset transfer series is still a work in progress. Additional smart contract and application languages are being developed. The series will also be integrated with other Fabric samples and the documentation in the near future. For more information, see the public plan for [Fabric samples improvements](https://docs.google.com/presentation/d/1UxK2HH8SrQyZU58MnuDb9hr1nmekst8b/edit#slide=id.g776cdbfb06_0_51). + ## License Hyperledger Project source code files are made available under the Apache diff --git a/asset-transfer-secured-agreement/chaincode-go/README.md b/asset-transfer-secured-agreement/chaincode-go/README.md index a2559f6b..36c39ed8 100644 --- a/asset-transfer-secured-agreement/chaincode-go/README.md +++ b/asset-transfer-secured-agreement/chaincode-go/README.md @@ -7,13 +7,13 @@ Each on-chain asset is a non-fungible token (NFT) that represents a specific ass The private asset transfer scenario is bound by the following requirements: -- A asset may be issued by the first owner's organization (in the real world issuance may be restricted to some authority that certifies a asset's properties). +- An asset may be issued by the first owner's organization (in the real world issuance may be restricted to some authority that certifies an asset's properties). - Ownership is managed at the organization level (the Fabric permissioning scheme would equally support ownership at an individual identity level within an organization). - The asset identifier and owner is stored as public channel data for all channel members to see. - The asset metadata properties however are private information known only to the asset owner (and prior owners). -- An interested buyer will want to verify a asset's private properties. -- An interested buyer will want to verify a asset's provenance, specifically the asset's origin and chain of custody. They will also want to verify that the asset has not changed since issuance, and that all prior transfers have been legitimate. -- To transfer a asset, a buyer and seller must first agree on a sales price. +- An interested buyer will want to verify an asset's private properties. +- An interested buyer will want to verify an asset's provenance, specifically the asset's origin and chain of custody. They will also want to verify that the asset has not changed since issuance, and that all prior transfers have been legitimate. +- To transfer an asset, the buyer and the seller must first agree on a sales price. - Only the current owner may transfer their asset to another organization. - The actual private asset transfer must verify that the legitimate asset is being transferred, and verify that the price has been agreed to. Both buyer and seller must endorse the transfer. From 31cce9e330af05826e0cbfa47ed1b1917495793f Mon Sep 17 00:00:00 2001 From: stephyee Date: Wed, 22 Jul 2020 14:45:57 -0400 Subject: [PATCH 12/45] Update secured asset sample tutorial link and remove README (#257) Signed-off-by: Tiffany Harris --- README.md | 2 +- .../chaincode-go/README.md | 343 +----------------- 2 files changed, 2 insertions(+), 343 deletions(-) diff --git a/README.md b/README.md index 5483167e..9c0877f9 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The asset transfer series provides a series of smart contracts and applications | [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. | **Coming soon** | Go, JavaScript, Typescript | JavaScript, Typescript | | [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates how to deploy an index with your chaincode and issue rich queries when you are using CouchDB as your state database. | **Coming soon** | Go | **Coming soon** | | [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections and how the private data hash can be used to verify an agreement before executing a transfer | **Coming soon** | Go | **Coming soon** | -| [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | **Coming soon** | Go | **Coming soon** | +| [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_private_asset_transfer_tutorial.html) | Go | **Coming soon** | The asset transfer series is still a work in progress. Additional smart contract and application languages are being developed. The series will also be integrated with other Fabric samples and the documentation in the near future. For more information, see the public plan for [Fabric samples improvements](https://docs.google.com/presentation/d/1UxK2HH8SrQyZU58MnuDb9hr1nmekst8b/edit#slide=id.g776cdbfb06_0_51). diff --git a/asset-transfer-secured-agreement/chaincode-go/README.md b/asset-transfer-secured-agreement/chaincode-go/README.md index 36c39ed8..87c86bcf 100644 --- a/asset-transfer-secured-agreement/chaincode-go/README.md +++ b/asset-transfer-secured-agreement/chaincode-go/README.md @@ -1,342 +1 @@ -# Secured private asset transfer scenario - -The private asset transfer smart contract demonstrates how an asset can be represented and traded between organizations in a Hyperledger Fabric blockchain channel, while keeping details of the asset and transaction private. -Each on-chain asset is a non-fungible token (NFT) that represents a specific asset having certain immutable metadata properties (such as size and color) with a unique owner. When the owner wants to sell the asset, both parties need to agree to the same price before the asset is transferred. The private asset transfer smart contract enforces that only the owner of the asset can transfer the asset. In the course of this tutorial, you will learn how Fabric features such as state based endorsement, private data, and access control come together to provide secured transactions that are both private and verifiable. - -## Scenario requirements - -The private asset transfer scenario is bound by the following requirements: - -- An asset may be issued by the first owner's organization (in the real world issuance may be restricted to some authority that certifies an asset's properties). -- Ownership is managed at the organization level (the Fabric permissioning scheme would equally support ownership at an individual identity level within an organization). -- The asset identifier and owner is stored as public channel data for all channel members to see. -- The asset metadata properties however are private information known only to the asset owner (and prior owners). -- An interested buyer will want to verify an asset's private properties. -- An interested buyer will want to verify an asset's provenance, specifically the asset's origin and chain of custody. They will also want to verify that the asset has not changed since issuance, and that all prior transfers have been legitimate. -- To transfer an asset, the buyer and the seller must first agree on a sales price. -- Only the current owner may transfer their asset to another organization. -- The actual private asset transfer must verify that the legitimate asset is being transferred, and verify that the price has been agreed to. Both buyer and seller must endorse the transfer. - -## How privacy is maintained - -The smart contract uses the following techniques to ensure that the asset properties remain private: - -- The asset metadata properties are stored in the current owning organization's implicit private data collection on the organization's peers only. Each organization on a Fabric channel has a private data collection that their own organization can use. This collection is *implicit* because it does not need to be explicitly defined in the chaincode. -- Although a hash of the private properties is automatically stored on-chain for all channel members to see, a random salt is included in the private properties so that other channel members cannot guess the private data preimage through a dictionary attack. -- Smart contract requests utilize the transient field for private data so that private data does not get included in the final on-chain transaction. -- Private data queries must originate from a client whose org id matches the peer's org id, which must be the same as the asset owner's org id. - -## How the transfer is implemented - -Before we start using the private asset transfer smart contract we will provide an overview of the transaction flow and how Fabric features are used to protect the asset created on the blockchain: - -**Step 1: Creating the asset** - -The private asset transfer smart contract is deployed with an endorsement policy that requires an endorsement from any channel member. This allows any organization to create a asset that they own without requiring an endorsement from other channel members. The creation of the asset is the only transaction that uses the chaincode level endorsement policy. Transactions that update or transfer existing assets will be governed by state based endorsement policies or the endorsement policies of private data collections. Note that in other scenarios, you may want an issuing authority to also endorse create transactions. - -The smart contract uses the following Fabric features to ensure that the asset can only be updated or transferred by the organization that owns the asset: - -- When the asset is created, the smart contract gets the MSP ID of the organization that submitted the request, and stores the MSP ID as the owner in the asset key/value in the public chaincode world state. Subsequent smart contract requests to update or transfer the asset will use access control logic to verify that the requesting client is from the same organization. Note that in other scenarios, the ownership could be based on a specific client identity within an organization, rather than an organization itself. -- Also when the asset is created, the smart contract sets a state based endorsement policy for the asset key. The state based policy specifies that a peer from the organization that owns the asset must endorse a subsequent request to update or transfer the asset. This prevents any other organization from updating or transferring the asset using a smart contract that has been maliciously altered on their own peers. - -**Step 2: Agreeing to the transfer.** - -After a asset is created, channel members can use the smart contract to agree to transfer the asset: - -- The owner of the asset can change the description in the public ownership record, for example to advertise that the asset is for sale. Smart contract access control enforces that this change needs to be submitted from a member of the asset owner organization. The state based endorsement policy enforces that this description change must be endorsed by a peer from the owner's organization. - - -The asset owner and the asset buyer agree to transfer the asset for a certain price: -- The price agreed to by the buyer and the seller is stored in each organization's implicit private data collection. The private data collection keeps the agreed price secret from other members of the channel. The endorsement policy of the private data collection ensures that the respective organization's peer endorsed the price agreement, and the smart contract access control logic ensures that the price agreement was submitted by a client of the associated organization. -- A hash of each price agreement is stored on the ledger. The two hashes will match only if the two organizations have agreed to the same price. This allows the organizations to verify that they have come to agreement on the transfer details before the transfer takes place. A random trade id is added to the price agreement, which serves as a *salt* to ensure that other channel members can not use the hash on the ledger to guess the price. - -**Step 3: Transferring the asset** - -After the two organizations have agreed to the same price, the asset owner can use the transfer function to transfer the asset to the buyer: - -- Smart contract access control ensures that the transfer must be initiated by a member of the organization that owns the asset. -- The transfer function verifies that the asset details passed to the transfer function matches the on chain hash, to ensure that the asset owner is *selling* the same asset that they own. -- The transfer function uses the hash of the price agreement on the ledger to ensure that both organizations have agreed to the same price. -- If the transfer conditions are met, the transfer function adds the asset to the implicit private data collection of the buyer, and deletes the asset from the collection of the seller. The transfer also updates the owner in the public ownership record. -- Because of the endorsement policies of the seller and buyer implicit data collections, and the state based endorsement policy of the public record (requiring the seller to endorse), the transfer needs to be endorsed by peers from both buyer and seller. -- The state based endorsement policy of the public asset record is updated so that only a peer of the new owner of the asset can update or sell their new asset. - -## Running the private asset transfer smart contract - -You can use the Fabric test network to run the private asset transfer smart contract. The test network contains two peer organizations, Org1 and Org1, that operate one peer each. In this tutorial, we will deploy the smart contract to a channel of the test network joined by both organizations. We will first create a asset that is owned by Org1. After the two organizations agree on a asset price, we will transfer the asset from Org1 to Org2. - -## Deploy the test network - -We need to deploy an instance of the Fabric test network to run the smart contract. 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 -``` - -You can then deploy the network with the following command: -``` -./network.sh up createChannel -``` -The script will deploy the nodes of the network create a single channel named `mychannel` with Org1 and Org2 as channel members. We will use this channel to deploy the smart contract and trade our asset. - -### Set the environment variables to operate as Org1 - -In the course of running this sample, you need to interact with the network as both Org1 and Org2. To make the tutorial easier to use, we will use separate terminals for each organization. Open a new terminal and make sure that you are operating from the `test-network` directory. Set the following environment variables to operate the `peer` CLI as the Org1 admin: -``` -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/Admin@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 -``` -The environment variables also specify the endpoint information of the Org1 peer to submit requests. - -### Set the environment variables to operate as Org2 - -Now that we have one terminal that we can operate as Org1, open a new terminal for Org2. Make sure that this terminal is also operating from the `test-network` directory. Set the following environment variables to operate as the Org2 admin: -``` -export PATH=${PWD}/../bin:${PWD}:$PATH -export FABRIC_CFG_PATH=$PWD/../config/ -export CORE_PEER_TLS_ENABLED=true -export CORE_PEER_LOCALMSPID="Org2MSP" -export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -export CORE_PEER_ADDRESS=localhost:9051 -``` - -You will need switch between the two terminals as you go through the tutorial. - -## Deploy the chaincode - -Now that we can operate as both organizations, we need install the private asset transfer smart contract on the peers of Org1 and Org2, and deploy the chaincode to the channel approving and committing the chaincode definition. - -### Install and approve the chaincode as Org1 - -Open the Org1 terminal. Run the following command to package the private asset transfer chaincode: -``` -peer lifecycle chaincode package assets_transfer.tar.gz --path ../asset-transfer-secured-agreement/chaincode-go --lang golang --label assets_transfer_1 -``` - -The command creates a chaincode package named `assets_transfer.tar.gz`. We can now install this package on the Org1 peer: -``` -peer lifecycle chaincode install assets_transfer.tar.gz -``` - -You will need the chaincode package ID in order to approve the chaincode definition. You can find the package ID by querying your peer: -``` -peer lifecycle chaincode queryinstalled -``` -Save the package ID as an environment variable. The package ID will not be the same for all users, so need to use the result that was returned by the previous command: -``` -export PACKAGE_ID=assets_transfer_1:2a585633baa0a6ba0019965ac40d6f188194c50df1015010b080ef6ba426d266 -``` -You can now approve the chaincode as Org1: -``` -peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name assets_transfer --version 1 --package-id $PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` - -Note we are approving a chaincode endorsement policy of `"OR('Org1MSP.peer','Org2MSP.peer')"`. This allows either organization to create a asset without receiving an endorsement from the other organization. - -### Install and approve the chaincode as Org2 - -We can now install and approve the chaincode as Org2. Open the Org2 terminal. Because the chaincode is already packaged on our local machine, we can go ahead and install the chaincode on the Org2 peer:` -``` -peer lifecycle chaincode install assets_transfer.tar.gz -``` -Query the package ID of the chaincode: -``` -peer lifecycle chaincode queryinstalled -``` - -Save the result of the command as an environment variable in the Org2 command window: -``` -export PACKAGE_ID=assets_transfer_1:2a585633baa0a6ba0019965ac40d6f188194c50df1015010b080ef6ba426d266 -``` - -We can now approve the chaincode as the Org2 admin: -``` -peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name assets_transfer --version 1 --package-id $PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` - -Now that a majority (2 out of 2) of channel members have approved the chaincode definition, Org2 can commit the chaincode definition to deploy the chaincode to the channel: -``` -peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name assets_transfer --version 1 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --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 --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` -We are now ready use the private asset transfer smart contract. - -## Create a asset - -Any channel member can use the smart contract to create a asset that is owned by their organization. The details of the asset will be stored in a private data collection, and can only accessed by the organization that owns the asset. A public record of the asset, its owner, and a public description is stored on the channel ledger. Any channel member can access the public ownership record to see who owns the asset, and can read the description to see if the asset is for sale. - -### Operate from the Org1 terminal - -Before we create the asset, we need to specify the details of what our asset will be. Issue the following command to create a JSON that will describe the asset. The `"salt"` parameter is a random string that would prevent another member of the channel from guessing the asset using the hash on the ledger. If there was no salt, a user could theoretically guess asset parameters until the hash of the of the guess and the hash on the ledger matched (this is known as a dictionary attack). This string is encoded in Base64 format so that it can be passed to the creation transaction as transient data. -``` -export asset_PROPERTIES=$(echo -n "{\"object_type\":\"asset_properties\",\"asset_id\":\"asset1\",\"color\":\"blue\",\"size\":35,\"salt\":\"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\"}" | base64 | tr -d \\n) -``` -We can now use the following command to create a asset that belongs to Org1: -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"CreateAsset","Args":["asset1", "A new asset for Org1MSP"]}' --transient "{\"asset_properties\":\"$asset_PROPERTIES\"}" -``` - -We can can query the Org1 implicit data collection to see the asset that was created: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"GetAssetPrivateProperties","Args":["asset1"]}' -``` - -When successful, the command will return the following result: -``` -{"object_type":"asset_properties","asset_id":"asset1","color":"blue","size":35,"salt":"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"} -``` - -We can also query the ledger to see the public ownership record: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' -``` -The command will return the record that the asset1 is owned by Org1: -``` -{"object_type":"asset","asset_id":"asset1","owner_org":"Org1MSP","public_description":"A new asset for Org1MSP"} -``` -Because the market for assets is hot, Org1 wants to flip this asset and put it up for sale. As the asset owner, Org1 can update the public description to advertise that the asset is for sale. Run the following command to change the asset description: -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"ChangePublicDescription","Args":["asset1","This asset is for sale"]}' -``` -Query the ledger again to see the updated description: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' -``` -We can now see that the asset is for sale: -``` -{"object_type":"asset","asset_id":"asset1","owner_org":"Org1MSP","public_description":"This asset is for sale"} -``` - - ![Org1 creates a asset](images/transfer_assets_1.png) -*Figure 1: When Org1 creates a asset that they own, the asset details are stored in the Org1 implicit data collection on the Org1 peer. The public ownership record is stored in the channel world state, and is stored on both the Org1 and Org2 peers. A hash of the asset key and a hash the asset details are also visible in the channel world state and are stored on the peers of both organizations.* - -### Operate from the Org2 terminal - -If we operate from the Org2 terminal, we can use the smart contract query the public asset data: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' -``` -From this query, Org2 learns that asset1 is for sale: -``` -{"object_type":"asset","asset_id":"asset1","owner_org":"Org1MSP","public_description":"This asset is for sale"} -``` - -Any changes to the public description of the asset owned by Org1 needs to be endorsed by Org1. The endorsement policy is reinforced by an access control policy within the chaincode that any updated need to be submitted by the organization that owns the asset. Lets see what happens if Org2 tried to change the public description as a prank: -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"ChangePublicDescription","Args":["asset1","the worst asset"]}' -``` -The smart contract does not allow Org2 to access the public description of the asset. -``` -Error: endorsement failure during invoke. response: status:500 message:"a client from Org2MSP cannot update the description of a asset owned by Org1MSP" -``` - -## Agree to sell the asset - -To sell a asset, both the buyer and the seller must agree on a asset price. Each party stores the price that they agree to in their own private data collection. The private asset transfer smart contract enforces that both parties need to agree to the same price before the asset can be transferred. - -## Agree to sell as Org1 - -Operate from the Org1 terminal. Org1 will agree to set the asset price as 110 dollars. The `trade_id` is used as salt to prevent a channel member that is not a buyer or a seller from guessing the price. This value needs to be passed out of band, through email or other communication, between the buyer and the seller. The buyer and the seller can also add salt to the asset key to prevent other members of the channel from guessing which asset is for sale. -``` -export asset_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":110}" | base64) -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"AgreeToSell","Args":["asset1"]}' --transient "{\"asset_price\":\"$asset_PRICE\"}" -``` - -We can query the Org1 private data collection to read the agreed to selling price: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"GetAssetSalesPrice","Args":["asset1"]}' -``` - -## Agree to buy as Org2 - -Operate from the Org2 terminal. Run the following command to verify the asset properties before agreeing to buy. The asset properties and salt would be passed out of band, through email or other communication, between the buyer and seller. -``` -export asset_PROPERTIES=$(echo -n "{\"object_type\":\"asset_properties\",\"asset_id\":\"asset1\",\"color\":\"blue\",\"size\":35,\"salt\":\"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\"}" | base64) -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"VerifyAssetProperties","Args":["asset1"]}' --transient "{\"asset_properties\":\"$asset_PROPERTIES\"}" -``` - -Run the following command to agree to buy asset1 for 100 dollars. As of now, Org2 will agree to a different price than Org2. Don't worry, the two organizations will agree to the same price in a future step. However, we we can use this temporary disagreement as a test of what happens if the buyer and the seller agree to a different price. Org2 needs to use the same `trade_id` as Org1. -``` -export asset_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":100}" | base64) -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"AgreeToBuy","Args":["asset1"]}' --transient "{\"asset_price\":\"$asset_PRICE\"}" -``` -You can read the agreed purchase price from the Org2 implicit data collection: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"GetAssetBidPrice","Args":["asset1"]}' -``` - - ![Org1 and Org2 agree on transfer](images/transfer_assets_2.png) -*Figure 2: After Org1 and Org2 agree to transfer the asset, the price agreed to by each organization is stored in their private data collections. A composite key for the seller and the buyer is used to prevent a collision with the asset details and asset ownership record. The price that is agreed to is only stored on the peers of each organization. However, the hash of both agreements is stored in the channel world state on every peer joined to the channel.* - -## Transfer the asset from to Org2 - -After both organizations have agreed to their price, Org1 can attempt to transfer the asset to Org2. The private asset transfer function in the smart contract uses the hash on the ledger to check that both organizations have agreed to the same price. The function will also use the hash of the private asset details to check that the asset that is transferred is the same asset that Org1 owns. - -### Transfer the asset as Org1 - -Operate from the Org1 terminal. The owner of the asset needs to initiate the transfer. Note that the command below uses the `--peerAddresses` flag to target the peers of both Org1 and Org2. Both organizations need to endorse the transfer. - -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"TransferAsset","Args":["asset1","Org2MSP"]}' --transient "{\"asset_properties\":\"$asset_PROPERTIES\",\"asset_price\":\"$asset_PRICE\"}" --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 -``` -Because the two organizations have not agreed to the same price, the transfer cannot be completed: -``` -Error: endorsement failure during invoke. response: status:500 message:"failed transfer verification: hash cf74b8ce092b637bd28f98f7cdd490534c102a0665e7c985d4f2ab9810e30b1c for passed price JSON {\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":110} does not match on-chain hash 09341dbb39e81fb50ccb3a81770254525318f777fad217ae49777487116cceb4, buyer hasn't agreed to the passed trade id and price" -``` - -As a result, Org1 and Org2 come to a new agreement on the price at which the asset will be purchased. Org1 drops the price of the asset to 100: -``` -export asset_PRICE=$(echo -n "{\"asset_id\":\"asset1\",\"trade_id\":\"109f4b3c50d7b0df729d299bc6f8e9ef9066971f\",\"price\":100}" | base64) -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"AgreeToSell","Args":["asset1"]}' --transient "{\"asset_price\":\"$asset_PRICE\"}" -``` - -Now that the buyer and seller have agreed to the same price, Org1 can transfer the asset to Org2. -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"TransferAsset","Args":["asset1","Org2MSP"]}' --transient "{\"asset_properties\":\"$asset_PROPERTIES\",\"asset_price\":\"$asset_PRICE\"}" --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 -``` - -You can query the asset ownership record to verify that the transfer was successful. - -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' -``` - -The record now lists Org2 as the asset owner: -``` -{"object_type":"asset","asset_id":"asset1","owner_org":"Org2MSP","public_description":"This asset is for sale"} -``` - - ![Org1 transfers the asset to Org2](images/transfer_assets_3.png) -*Figure 3: After the asset is transferred, the asset details are placed in the Org2 implicit data collection and deleted from the Org1 implicit data collection. As a result, the asset details are now only stored on the Org2 peer. The asset ownership record on the ledger is updated to reflect that the asset is owned by Org1.* - -### Update the asset description as Org2 - -Operate from the Org2 terminal. Now that Org2 owns the asset, we can read the asset details from the Org2 implicit data collection: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"GetAssetPrivateProperties","Args":["asset1"]}' -``` - -Org2 can now update the asset public description: -``` -peer chaincode invoke -o localhost:7050 --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 assets_transfer -c '{"function":"ChangePublicDescription","Args":["asset1","This asset is not for sale"]}' -``` - -Query the ledger to verify that the asset is no longer for sale: -``` -peer chaincode query -o localhost:7050 --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 assets_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' -``` - -## Clean up - -When you are finished transferring assets, 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 -``` +[Secured asset transfer in Fabric Tutorial](https://hyperledger-fabric.readthedocs.io/en/master/secured_private_asset_transfer_tutorial.html) From 7a23d7872a700352b99161fb0edd60e59cc866d7 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Wed, 22 Jul 2020 15:57:54 -0400 Subject: [PATCH 13/45] Remove Typescript References (#258) As the Typescript examples are essentially just reiterations of the same Javascript code, there is no benefit to having providing examples in both languages. The functional code was exactly the same in both languages. On the contrary it meant we widened the surface of maitainence and thus we are removing due to the limited benefit provided by the example chaincode and applications. Signed-off-by: Brett Logan --- README.md | 2 +- .../application-typescript/.gitignore | 17 -- .../application-typescript/package.json | 59 ------- .../application-typescript/src/enrollAdmin.ts | 56 ------- .../application-typescript/src/invoke.ts | 59 ------- .../application-typescript/src/query.ts | 57 ------- .../src/registerUser.ts | 68 -------- .../application-typescript/tsconfig.json | 16 -- .../application-typescript/tslint.json | 22 --- .../chaincode-typescript/.gitignore | 16 -- .../chaincode-typescript/package.json | 62 ------- .../chaincode-typescript/src/asset.ts | 12 -- .../chaincode-typescript/src/assetTransfer.ts | 153 ------------------ .../chaincode-typescript/src/index.ts | 8 - .../chaincode-typescript/tsconfig.json | 16 -- .../chaincode-typescript/tslint.json | 21 --- chaincode/README.md | 2 +- chaincode/abstore/javascript/.gitignore | 3 - chaincode/fabcar/javascript/.gitignore | 3 - chaincode/fabcar/typescript/.editorconfig | 16 -- chaincode/fabcar/typescript/.gitignore | 81 ---------- chaincode/fabcar/typescript/package.json | 62 ------- chaincode/fabcar/typescript/src/car.ts | 11 -- chaincode/fabcar/typescript/src/fabcar.ts | 140 ---------------- chaincode/fabcar/typescript/src/index.ts | 8 - chaincode/fabcar/typescript/tsconfig.json | 16 -- chaincode/fabcar/typescript/tslint.json | 21 --- chaincode/marbles02/javascript/.gitignore | 3 - ci/azure-pipelines.yml | 8 - .../fabcar/azure-pipelines-typescript.yml | 22 --- ci/templates/test-network/azure-pipelines.yml | 1 - .../organization/digibank/contract/.npmignore | 3 - .../magnetocorp/contract/.npmignore | 3 - fabcar/javascript/.gitignore | 3 - fabcar/networkDown.sh | 1 - fabcar/startFabric.sh | 30 +--- fabcar/typescript/.editorconfig | 16 -- fabcar/typescript/.gitignore | 83 ---------- fabcar/typescript/package.json | 59 ------- fabcar/typescript/src/enrollAdmin.ts | 52 ------ fabcar/typescript/src/invoke.ts | 53 ------ fabcar/typescript/src/query.ts | 51 ------ fabcar/typescript/src/registerUser.ts | 64 -------- fabcar/typescript/tsconfig.json | 16 -- fabcar/typescript/tslint.json | 22 --- fabcar/typescript/wallet/.gitkeep | 0 test-network/network.sh | 2 +- test-network/scripts/deployCC.sh | 14 +- 48 files changed, 5 insertions(+), 1508 deletions(-) delete mode 100644 asset-transfer-basic/application-typescript/.gitignore delete mode 100644 asset-transfer-basic/application-typescript/package.json delete mode 100644 asset-transfer-basic/application-typescript/src/enrollAdmin.ts delete mode 100644 asset-transfer-basic/application-typescript/src/invoke.ts delete mode 100644 asset-transfer-basic/application-typescript/src/query.ts delete mode 100644 asset-transfer-basic/application-typescript/src/registerUser.ts delete mode 100644 asset-transfer-basic/application-typescript/tsconfig.json delete mode 100644 asset-transfer-basic/application-typescript/tslint.json delete mode 100644 asset-transfer-basic/chaincode-typescript/.gitignore delete mode 100644 asset-transfer-basic/chaincode-typescript/package.json delete mode 100644 asset-transfer-basic/chaincode-typescript/src/asset.ts delete mode 100644 asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts delete mode 100644 asset-transfer-basic/chaincode-typescript/src/index.ts delete mode 100644 asset-transfer-basic/chaincode-typescript/tsconfig.json delete mode 100644 asset-transfer-basic/chaincode-typescript/tslint.json delete mode 100755 chaincode/fabcar/typescript/.editorconfig delete mode 100644 chaincode/fabcar/typescript/.gitignore delete mode 100644 chaincode/fabcar/typescript/package.json delete mode 100644 chaincode/fabcar/typescript/src/car.ts delete mode 100644 chaincode/fabcar/typescript/src/fabcar.ts delete mode 100644 chaincode/fabcar/typescript/src/index.ts delete mode 100644 chaincode/fabcar/typescript/tsconfig.json delete mode 100644 chaincode/fabcar/typescript/tslint.json delete mode 100644 ci/templates/fabcar/azure-pipelines-typescript.yml delete mode 100755 fabcar/typescript/.editorconfig delete mode 100644 fabcar/typescript/.gitignore delete mode 100644 fabcar/typescript/package.json delete mode 100644 fabcar/typescript/src/enrollAdmin.ts delete mode 100644 fabcar/typescript/src/invoke.ts delete mode 100644 fabcar/typescript/src/query.ts delete mode 100644 fabcar/typescript/src/registerUser.ts delete mode 100644 fabcar/typescript/tsconfig.json delete mode 100644 fabcar/typescript/tslint.json delete mode 100644 fabcar/typescript/wallet/.gitkeep diff --git a/README.md b/README.md index 9c0877f9..c86caa91 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The asset transfer series provides a series of smart contracts and applications | **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. | **Coming soon** | Go, JavaScript, Typescript | JavaScript, Typescript | +| [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. | **Coming soon** | Go, JavaScript | JavaScript | | [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates how to deploy an index with your chaincode and issue rich queries when you are using CouchDB as your state database. | **Coming soon** | Go | **Coming soon** | | [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections and how the private data hash can be used to verify an agreement before executing a transfer | **Coming soon** | Go | **Coming soon** | | [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_private_asset_transfer_tutorial.html) | Go | **Coming soon** | diff --git a/asset-transfer-basic/application-typescript/.gitignore b/asset-transfer-basic/application-typescript/.gitignore deleted file mode 100644 index e610e389..00000000 --- a/asset-transfer-basic/application-typescript/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -# Coverage directory used by tools like istanbul -coverage - -# Dependency directories -node_modules/ -jspm_packages/ -package-lock.json - -# Compiled TypeScript files -dist - -wallet -!wallet/.gitkeep diff --git a/asset-transfer-basic/application-typescript/package.json b/asset-transfer-basic/application-typescript/package.json deleted file mode 100644 index 03546d28..00000000 --- a/asset-transfer-basic/application-typescript/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "asset-transfer-basic", - "version": "1.0.0", - "description": "Asset-Transfer-Basic application implemented in TypeScript", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "scripts": { - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "pretest": "npm run lint", - "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", - "build": "tsc", - "build:watch": "tsc -w", - "prepublishOnly": "npm run build" - }, - "engineStrict": true, - "author": "Hyperledger", - "license": "Apache-2.0", - "dependencies": { - "fabric-ca-client": "^2.1.0", - "fabric-network": "^2.1.0" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.5", - "@types/node": "^10.12.10", - "@types/sinon": "^5.0.7", - "@types/sinon-chai": "^3.2.1", - "chai": "^4.2.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0", - "ts-node": "^7.0.1", - "tslint": "^5.11.0", - "typescript": "^3.1.6" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "coverage/**", - "dist/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 - } -} diff --git a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts deleted file mode 100644 index 43076c06..00000000 --- a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as FabricCAServices from 'fabric-ca-client'; -import { Wallets, X509Identity } from 'fabric-network'; -import * as fs from 'fs'; -import * as path from 'path'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; - const caTLSCACerts = caInfo.tlsCACerts.pem; - const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the admin user. - const identity = await wallet.get('admin'); - if (identity) { - console.log('An identity for the admin user "admin" already exists in the wallet'); - return; - } - - // Enroll the admin user, and import the new identity into the wallet. - const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('admin', x509Identity); - console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to enroll admin user "admin": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/invoke.ts b/asset-transfer-basic/application-typescript/src/invoke.ts deleted file mode 100644 index 205f3212..00000000 --- a/asset-transfer-basic/application-typescript/src/invoke.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Submit the specified transaction. (several example transactions are listed below) - // createAsset creates an asset with ID asset1, color yellow, owner Dave, size 5 and appraizedValue of 130 requires 6 arguments. - // ex: ('createAsset', 'asset1', 'yellow', 'Dave', 5, 1300) - // transferAsset transfers an asset with ID asset1 to new owner Tom - requires 2 arguments. - // ex: ('transferAsset', 'asset1', 'Tom') - await contract.submitTransaction('createAsset', 'asset13', 'yellow', "5", 'Tom', "1300"); - console.log(`Transaction has been submitted`); - - // Disconnect from the gateway. - await gateway.disconnect(); - - } catch (error) { - console.error(`Failed to submit transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/query.ts b/asset-transfer-basic/application-typescript/src/query.ts deleted file mode 100644 index aae2243f..00000000 --- a/asset-transfer-basic/application-typescript/src/query.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Evaluate the specified transaction. (several example transactions are listed below) - // getAsset returns an asset with given assetID asset4 - requires 1 argument. - // ex: ('getAsset', 'asset4') - // getAllAssets returns all assets in the world state - requires no arguments. - // ex: ('getAllAssets') - const result = await contract.evaluateTransaction('getAllAssets'); - console.log(`Transaction has been evaluated, result is: ${result.toString()}`); - - } catch (error) { - console.error(`Failed to evaluate transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/registerUser.ts b/asset-transfer-basic/application-typescript/src/registerUser.ts deleted file mode 100644 index 0b6b7a2c..00000000 --- a/asset-transfer-basic/application-typescript/src/registerUser.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Wallets, X509Identity } from 'fabric-network'; -import * as FabricCAServices from 'fabric-ca-client'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; - const ca = new FabricCAServices(caURL); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const userIdentity = await wallet.get('appUser'); - if (userIdentity) { - console.log('An identity for the user "appUser" already exists in the wallet'); - return; - } - - // Check to see if we've already enrolled the admin user. - const adminIdentity = await wallet.get('admin'); - if (!adminIdentity) { - console.log('An identity for the admin user "admin" does not exist in the wallet'); - console.log('Run the enrollAdmin.ts application before retrying'); - return; - } - - // build a user object for authenticating with the CA - const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); - const adminUser = await provider.getUserContext(adminIdentity, 'admin'); - - // Register the user, enroll the user, and import the new identity into the wallet. - const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); - const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('appUser', x509Identity); - console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to register user "appUser": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/tsconfig.json b/asset-transfer-basic/application-typescript/tsconfig.json deleted file mode 100644 index 8c96ea07..00000000 --- a/asset-transfer-basic/application-typescript/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "es2017", - "moduleResolution": "node", - "module": "commonjs", - "declaration": true, - "sourceMap": true - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./src/**/*.spec.ts" - ] -} diff --git a/asset-transfer-basic/application-typescript/tslint.json b/asset-transfer-basic/application-typescript/tslint.json deleted file mode 100644 index e08fd0b8..00000000 --- a/asset-transfer-basic/application-typescript/tslint.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [true, "spaces", 4], - "linebreak-style": [true, "LF"], - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "no-console": false, - "curly": true, - "triple-equals": true, - "no-string-throw": true, - "no-var-keyword": true, - "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"], - "max-line-length": false - }, - "rulesDirectory": [] -} diff --git a/asset-transfer-basic/chaincode-typescript/.gitignore b/asset-transfer-basic/chaincode-typescript/.gitignore deleted file mode 100644 index 79bfe1a3..00000000 --- a/asset-transfer-basic/chaincode-typescript/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - - -# Coverage directory used by tools like istanbul -coverage - -# Dependency directories -node_modules/ -jspm_packages/ -package-lock.json - -# Compiled TypeScript files -dist - diff --git a/asset-transfer-basic/chaincode-typescript/package.json b/asset-transfer-basic/chaincode-typescript/package.json deleted file mode 100644 index 2b681fc1..00000000 --- a/asset-transfer-basic/chaincode-typescript/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "asset-transfer-basic", - "version": "1.0.0", - "description": "Asset Transfer Basic contract implemented in TypeScript", - "main": "dist/index.js", - "typings": "dist/index.d.ts", - "engines": { - "node": ">=12", - "npm": ">=5" - }, - "scripts": { - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "pretest": "npm run lint", - "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", - "start": "fabric-chaincode-node start", - "build": "tsc", - "build:watch": "tsc -w", - "prepublishOnly": "npm run build" - }, - "engineStrict": true, - "author": "Hyperledger", - "license": "Apache-2.0", - "dependencies": { - "fabric-contract-api": "^2.0.0", - "fabric-shim": "^2.0.0" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.5", - "@types/node": "^10.12.10", - "@types/sinon": "^5.0.7", - "@types/sinon-chai": "^3.2.1", - "chai": "^4.2.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0", - "ts-node": "^7.0.1", - "tslint": "^5.11.0", - "typescript": "^3.1.6" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "coverage/**", - "dist/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 - } -} diff --git a/asset-transfer-basic/chaincode-typescript/src/asset.ts b/asset-transfer-basic/chaincode-typescript/src/asset.ts deleted file mode 100644 index 56d33439..00000000 --- a/asset-transfer-basic/chaincode-typescript/src/asset.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -export class Asset { - public docType?: string; - public ID: string; - public Color: string; - public Size: number; - public Owner: string; - public AppraisedValue: number; -} diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts deleted file mode 100644 index 04ebcb99..00000000 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Context, Contract } from 'fabric-contract-api'; -import { Asset } from './asset'; - -export class AssetTransfer extends Contract { - - public async initLedger(ctx: Context) { - const assets: Asset[] = [ - { - ID: "asset1", - Color: "blue", - Size: 5, - Owner: "Tomoko", - AppraisedValue: 300, - }, - { - ID: "asset2", - Color: "red", - Size: 5, - Owner: "Brad", - AppraisedValue: 400, - }, - { - ID: "asset3", - Color: "green", - Size: 10, - Owner: "Jin Soo", - AppraisedValue: 500, - }, - { - ID: "asset4", - Color: "yellow", - Size: 10, - Owner: "Max", - AppraisedValue: 600, - }, - { - ID: "asset5", - Color: "black", - Size: 15, - Owner: "Adriana", - AppraisedValue: 700, - }, - { - ID: "asset6", - Color: "white", - Size: 15, - Owner: "Michel", - AppraisedValue: 800, - }, - ]; - - for (let i = 0; i < assets.length; i++) { - assets[i].docType = 'asset'; - await ctx.stub.putState(assets[i].ID, Buffer.from(JSON.stringify(assets[i]))); - console.info('Added <--> ', assets[i]); - } - } - - // createAsset issues a new asset to the world state with given details. - public async createAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { - const asset = { - ID: id, - Color: color, - Size: size, - Owner: owner, - AppraisedValue: appraisedValue, - }; - - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); - } - - // readAsset returns the asset stored in the world state with given id. - public async readAsset(ctx: Context, id: string): Promise { - const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state - if (!assetJSON || assetJSON.length === 0) { - throw new Error(`The asset ${id} does not exist`); - } - - return assetJSON.toString(); - } - - // updateAsset updates an existing asset in the world state with provided parameters. - public async updateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { - const exists = await this.assetExists(ctx, id); - if (!exists) { - throw new Error(`The asset ${id} does not exist`); - } - - // overwritting original asset with new asset - let updatedAsset = { - ID: id, - Color: color, - Size: size, - Owner: owner, - AppraisedValue: appraisedValue, - }; - - return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); - } - - // deleteAsset deletes an given asset from the world state. - public async deleteAsset(ctx: Context, id: string) { - const exists = await this.assetExists(ctx, id); - if (!exists) { - throw new Error(`The asset ${id} does not exist`); - } - - return ctx.stub.deleteState(id); - } - - // assetExists returns true when asset with given ID exists in world state. - public async assetExists(ctx: Context, id: string): Promise { - const assetJSON = await ctx.stub.getState(id); - if (!assetJSON || assetJSON.length === 0) { - return false; - } - return true; - } - - // transferAsset updates the owner field of asset with given id in the world state. - public async transferAsset(ctx: Context, id: string, newOwner: string) { - let assetString = await this.readAsset(ctx, id); - - let asset = JSON.parse(assetString); - asset.Owner = newOwner; - - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); - } - - // getAllAssets returns all assets found in the world state. - public async getAllAssets(ctx: Context): Promise { - const allResults = []; - // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for await (const { key, value } of ctx.stub.getStateByRange("", "")) { - const strValue = Buffer.from(value).toString('utf8'); - let record; - try { - record = JSON.parse(strValue); - } catch (err) { - console.log(err); - record = strValue; - } - allResults.push({ Key: key, Record: record }); - } - console.info(allResults); - return JSON.stringify(allResults); - } - -} diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts deleted file mode 100644 index a5f5e17b..00000000 --- a/asset-transfer-basic/chaincode-typescript/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { AssetTransfer } from './assetTransfer'; -export { AssetTransfer } from './assetTransfer'; - -export const contracts: any[] = [AssetTransfer]; diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json deleted file mode 100644 index 8c96ea07..00000000 --- a/asset-transfer-basic/chaincode-typescript/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "es2017", - "moduleResolution": "node", - "module": "commonjs", - "declaration": true, - "sourceMap": true - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./src/**/*.spec.ts" - ] -} diff --git a/asset-transfer-basic/chaincode-typescript/tslint.json b/asset-transfer-basic/chaincode-typescript/tslint.json deleted file mode 100644 index 33ccbf3c..00000000 --- a/asset-transfer-basic/chaincode-typescript/tslint.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [true, "spaces", 4], - "linebreak-style": [true, "LF"], - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "no-console": false, - "curly": true, - "triple-equals": true, - "no-string-throw": true, - "no-var-keyword": true, - "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"] - }, - "rulesDirectory": [] -} diff --git a/chaincode/README.md b/chaincode/README.md index ceeb0aab..603de32c 100644 --- a/chaincode/README.md +++ b/chaincode/README.md @@ -6,7 +6,7 @@ This folder contains example smart contracts that are used by the Hyperledger Fa | **Smart Contract** | **Description** | **Tutorial** | **Languages** | | -----------|------------------------------|----------|---------| -| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript, Typescript | +| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript | | [marbles02](marbles02) | Sample that demonstrates how to deploy an index and use rich queries when you are using CouchDB as your state database. | [Using CouchDB](https://hyperledger-fabric.readthedocs.io/en/master/couchdb_tutorial.html) | Go | | [marbles02_private](marbles02_private) | Sample that demonstrates the use of private data collections. | [Private data tutorial](https://hyperledger-fabric.readthedocs.io/en/master/private_data_tutorial.html) | Go | | [marbles_transfer](marbles_transfer) | Smart contract that demonstrates the use of private data, state based endorsement, and access control to securely transfer an asset between two parties | [Marbles private asset transfer scenario](marbles_transfer/README.md) | Go | diff --git a/chaincode/abstore/javascript/.gitignore b/chaincode/abstore/javascript/.gitignore index a00ca941..9f444c23 100644 --- a/chaincode/abstore/javascript/.gitignore +++ b/chaincode/abstore/javascript/.gitignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/chaincode/fabcar/javascript/.gitignore b/chaincode/fabcar/javascript/.gitignore index a00ca941..9f444c23 100644 --- a/chaincode/fabcar/javascript/.gitignore +++ b/chaincode/fabcar/javascript/.gitignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/chaincode/fabcar/typescript/.editorconfig b/chaincode/fabcar/typescript/.editorconfig deleted file mode 100755 index 75a13be2..00000000 --- a/chaincode/fabcar/typescript/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/chaincode/fabcar/typescript/.gitignore b/chaincode/fabcar/typescript/.gitignore deleted file mode 100644 index 69d6a33b..00000000 --- a/chaincode/fabcar/typescript/.gitignore +++ /dev/null @@ -1,81 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless - -# Compiled TypeScript files -dist - diff --git a/chaincode/fabcar/typescript/package.json b/chaincode/fabcar/typescript/package.json deleted file mode 100644 index 7cd3837c..00000000 --- a/chaincode/fabcar/typescript/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "fabcar", - "version": "1.0.0", - "description": "FabCar contract implemented in TypeScript", - "main": "dist/index.js", - "typings": "dist/index.d.ts", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "scripts": { - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "pretest": "npm run lint", - "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", - "start": "fabric-chaincode-node start", - "build": "tsc", - "build:watch": "tsc -w", - "prepublishOnly": "npm run build" - }, - "engineStrict": true, - "author": "Hyperledger", - "license": "Apache-2.0", - "dependencies": { - "fabric-contract-api": "^2.0.0", - "fabric-shim": "^2.0.0" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.5", - "@types/node": "^10.12.10", - "@types/sinon": "^5.0.7", - "@types/sinon-chai": "^3.2.1", - "chai": "^4.2.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0", - "ts-node": "^7.0.1", - "tslint": "^5.11.0", - "typescript": "^3.1.6" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "coverage/**", - "dist/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 - } -} diff --git a/chaincode/fabcar/typescript/src/car.ts b/chaincode/fabcar/typescript/src/car.ts deleted file mode 100644 index ba101625..00000000 --- a/chaincode/fabcar/typescript/src/car.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -export class Car { - public docType?: string; - public color: string; - public make: string; - public model: string; - public owner: string; -} diff --git a/chaincode/fabcar/typescript/src/fabcar.ts b/chaincode/fabcar/typescript/src/fabcar.ts deleted file mode 100644 index 597da7e2..00000000 --- a/chaincode/fabcar/typescript/src/fabcar.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Context, Contract } from 'fabric-contract-api'; -import { Car } from './car'; - -export class FabCar extends Contract { - - public async initLedger(ctx: Context) { - console.info('============= START : Initialize Ledger ==========='); - const cars: Car[] = [ - { - color: 'blue', - make: 'Toyota', - model: 'Prius', - owner: 'Tomoko', - }, - { - color: 'red', - make: 'Ford', - model: 'Mustang', - owner: 'Brad', - }, - { - color: 'green', - make: 'Hyundai', - model: 'Tucson', - owner: 'Jin Soo', - }, - { - color: 'yellow', - make: 'Volkswagen', - model: 'Passat', - owner: 'Max', - }, - { - color: 'black', - make: 'Tesla', - model: 'S', - owner: 'Adriana', - }, - { - color: 'purple', - make: 'Peugeot', - model: '205', - owner: 'Michel', - }, - { - color: 'white', - make: 'Chery', - model: 'S22L', - owner: 'Aarav', - }, - { - color: 'violet', - make: 'Fiat', - model: 'Punto', - owner: 'Pari', - }, - { - color: 'indigo', - make: 'Tata', - model: 'Nano', - owner: 'Valeria', - }, - { - color: 'brown', - make: 'Holden', - model: 'Barina', - owner: 'Shotaro', - }, - ]; - - for (let i = 0; i < cars.length; i++) { - cars[i].docType = 'car'; - await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i]))); - console.info('Added <--> ', cars[i]); - } - console.info('============= END : Initialize Ledger ==========='); - } - - public async queryCar(ctx: Context, carNumber: string): Promise { - const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state - if (!carAsBytes || carAsBytes.length === 0) { - throw new Error(`${carNumber} does not exist`); - } - console.log(carAsBytes.toString()); - return carAsBytes.toString(); - } - - public async createCar(ctx: Context, carNumber: string, make: string, model: string, color: string, owner: string) { - console.info('============= START : Create Car ==========='); - - const car: Car = { - color, - docType: 'car', - make, - model, - owner, - }; - - await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); - console.info('============= END : Create Car ==========='); - } - - public async queryAllCars(ctx: Context): Promise { - const startKey = ''; - const endKey = ''; - const allResults = []; - for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) { - const strValue = Buffer.from(value).toString('utf8'); - let record; - try { - record = JSON.parse(strValue); - } catch (err) { - console.log(err); - record = strValue; - } - allResults.push({ Key: key, Record: record }); - } - console.info(allResults); - return JSON.stringify(allResults); - } - - public async changeCarOwner(ctx: Context, carNumber: string, newOwner: string) { - console.info('============= START : changeCarOwner ==========='); - - const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state - if (!carAsBytes || carAsBytes.length === 0) { - throw new Error(`${carNumber} does not exist`); - } - const car: Car = JSON.parse(carAsBytes.toString()); - car.owner = newOwner; - - await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); - console.info('============= END : changeCarOwner ==========='); - } - -} diff --git a/chaincode/fabcar/typescript/src/index.ts b/chaincode/fabcar/typescript/src/index.ts deleted file mode 100644 index c0a2fcf6..00000000 --- a/chaincode/fabcar/typescript/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { FabCar } from './fabcar'; -export { FabCar } from './fabcar'; - -export const contracts: any[] = [ FabCar ]; diff --git a/chaincode/fabcar/typescript/tsconfig.json b/chaincode/fabcar/typescript/tsconfig.json deleted file mode 100644 index 8c96ea07..00000000 --- a/chaincode/fabcar/typescript/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "es2017", - "moduleResolution": "node", - "module": "commonjs", - "declaration": true, - "sourceMap": true - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./src/**/*.spec.ts" - ] -} diff --git a/chaincode/fabcar/typescript/tslint.json b/chaincode/fabcar/typescript/tslint.json deleted file mode 100644 index 33ccbf3c..00000000 --- a/chaincode/fabcar/typescript/tslint.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [true, "spaces", 4], - "linebreak-style": [true, "LF"], - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "no-console": false, - "curly": true, - "triple-equals": true, - "no-string-throw": true, - "no-var-keyword": true, - "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"] - }, - "rulesDirectory": [] -} diff --git a/chaincode/marbles02/javascript/.gitignore b/chaincode/marbles02/javascript/.gitignore index a00ca941..9f444c23 100644 --- a/chaincode/marbles02/javascript/.gitignore +++ b/chaincode/marbles02/javascript/.gitignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index dc336a0e..93bcee56 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -60,14 +60,6 @@ jobs: - template: templates/install-deps.yml - template: templates/fabcar/azure-pipelines-javascript.yml - - job: Fabcar_TypeScript - displayName: FabCar (TypeScript) - pool: - vmImage: ubuntu-18.04 - steps: - - template: templates/install-deps.yml - - template: templates/fabcar/azure-pipelines-typescript.yml - - job: TestNetwork displayName: Test Network pool: diff --git a/ci/templates/fabcar/azure-pipelines-typescript.yml b/ci/templates/fabcar/azure-pipelines-typescript.yml deleted file mode 100644 index 4e566f5b..00000000 --- a/ci/templates/fabcar/azure-pipelines-typescript.yml +++ /dev/null @@ -1,22 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -steps: - - script: ./startFabric.sh typescript - workingDirectory: fabcar - displayName: Start Fabric - - script: retry -- npm install - workingDirectory: fabcar/typescript - displayName: Install FabCar Application Dependencies - - script: npm run build - workingDirectory: fabcar/typescript - displayName: Build FabCar application - - script: | - set -ex - node dist/enrollAdmin - node dist/registerUser - node dist/invoke - node dist/query - workingDirectory: fabcar/typescript - displayName: Run FabCar Application diff --git a/ci/templates/test-network/azure-pipelines.yml b/ci/templates/test-network/azure-pipelines.yml index e934455c..f2c28cda 100644 --- a/ci/templates/test-network/azure-pipelines.yml +++ b/ci/templates/test-network/azure-pipelines.yml @@ -7,7 +7,6 @@ steps: ./network.sh up createChannel -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml ./network.sh deployCC -ccn basic -ccv 1 -ccl javascript -cci initLedger ./network.sh deployCC -ccn basic -ccv 2 -ccl golang -cci initLedger - ./network.sh deployCC -ccn basic -ccv 3 -ccl typescript -cci initLedger ./network.sh deployCC -ccn secure -ccv 1 -ccl golang ./network.sh down workingDirectory: test-network diff --git a/commercial-paper/organization/digibank/contract/.npmignore b/commercial-paper/organization/digibank/contract/.npmignore index a00ca941..9f444c23 100644 --- a/commercial-paper/organization/digibank/contract/.npmignore +++ b/commercial-paper/organization/digibank/contract/.npmignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/commercial-paper/organization/magnetocorp/contract/.npmignore b/commercial-paper/organization/magnetocorp/contract/.npmignore index a00ca941..9f444c23 100644 --- a/commercial-paper/organization/magnetocorp/contract/.npmignore +++ b/commercial-paper/organization/magnetocorp/contract/.npmignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/fabcar/javascript/.gitignore b/fabcar/javascript/.gitignore index 1dcaf1f2..a610a823 100644 --- a/fabcar/javascript/.gitignore +++ b/fabcar/javascript/.gitignore @@ -40,9 +40,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm diff --git a/fabcar/networkDown.sh b/fabcar/networkDown.sh index c1bc2313..74765e96 100755 --- a/fabcar/networkDown.sh +++ b/fabcar/networkDown.sh @@ -15,4 +15,3 @@ popd # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* -rm -rf typescript/wallet/* diff --git a/fabcar/startFabric.sh b/fabcar/startFabric.sh index 8ce18cb3..88edaed6 100755 --- a/fabcar/startFabric.sh +++ b/fabcar/startFabric.sh @@ -19,18 +19,15 @@ elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="../chaincode/fabcar/javascript/" elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_SRC_PATH="../chaincode/fabcar/java" -elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then - CC_SRC_PATH="../chaincode/fabcar/typescript/" else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, javascript, and typescript + echo Supported chaincode languages are: go, java, and javascript exit 1 fi # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* -rm -rf typescript/wallet/* rm -rf go/wallet/* # launch network; create channel and join peer to channel @@ -70,31 +67,6 @@ JavaScript: return all cars, but you can update the application to evaluate other transactions: node query -TypeScript: - - Start by changing into the "typescript" directory: - cd typescript - - Next, install all required packages: - npm install - - Next, compile the TypeScript code into JavaScript: - npm run build - - Then run the following applications to enroll the admin user, and register a new user - called appUser which will be used by the other applications to interact with the deployed - FabCar contract: - node dist/enrollAdmin - node dist/registerUser - - You can run the invoke application as follows. By default, the invoke application will - create a new car, but you can update the application to submit other transactions: - node dist/invoke - - You can run the query application as follows. By default, the query application will - return all cars, but you can update the application to evaluate other transactions: - node dist/query - Java: Start by changing into the "java" directory: diff --git a/fabcar/typescript/.editorconfig b/fabcar/typescript/.editorconfig deleted file mode 100755 index 75a13be2..00000000 --- a/fabcar/typescript/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/fabcar/typescript/.gitignore b/fabcar/typescript/.gitignore deleted file mode 100644 index 2a6c904c..00000000 --- a/fabcar/typescript/.gitignore +++ /dev/null @@ -1,83 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless - -# Compiled TypeScript files -dist - -wallet -!wallet/.gitkeep diff --git a/fabcar/typescript/package.json b/fabcar/typescript/package.json deleted file mode 100644 index 6cb67aa1..00000000 --- a/fabcar/typescript/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "fabcar", - "version": "1.0.0", - "description": "FabCar application implemented in TypeScript", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "scripts": { - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "pretest": "npm run lint", - "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", - "build": "tsc", - "build:watch": "tsc -w", - "prepublishOnly": "npm run build" - }, - "engineStrict": true, - "author": "Hyperledger", - "license": "Apache-2.0", - "dependencies": { - "fabric-ca-client": "^2.1.0", - "fabric-network": "^2.1.0" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.5", - "@types/node": "^10.12.10", - "@types/sinon": "^5.0.7", - "@types/sinon-chai": "^3.2.1", - "chai": "^4.2.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0", - "ts-node": "^7.0.1", - "tslint": "^5.11.0", - "typescript": "^3.1.6" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "coverage/**", - "dist/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 - } -} diff --git a/fabcar/typescript/src/enrollAdmin.ts b/fabcar/typescript/src/enrollAdmin.ts deleted file mode 100644 index e84139b8..00000000 --- a/fabcar/typescript/src/enrollAdmin.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as FabricCAServices from 'fabric-ca-client'; -import { Wallets, X509Identity } from 'fabric-network'; -import * as fs from 'fs'; -import * as path from 'path'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; - const caTLSCACerts = caInfo.tlsCACerts.pem; - const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(process.cwd(), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the admin user. - const identity = await wallet.get('admin'); - if (identity) { - console.log('An identity for the admin user "admin" already exists in the wallet'); - return; - } - - // Enroll the admin user, and import the new identity into the wallet. - const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('admin', x509Identity); - console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to enroll admin user "admin": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/fabcar/typescript/src/invoke.ts b/fabcar/typescript/src/invoke.ts deleted file mode 100644 index 762263ed..00000000 --- a/fabcar/typescript/src/invoke.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(process.cwd(), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('fabcar'); - - // Submit the specified transaction. - // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') - // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR12', 'Dave') - await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom'); - console.log(`Transaction has been submitted`); - - // Disconnect from the gateway. - await gateway.disconnect(); - - } catch (error) { - console.error(`Failed to submit transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/fabcar/typescript/src/query.ts b/fabcar/typescript/src/query.ts deleted file mode 100644 index b86677ee..00000000 --- a/fabcar/typescript/src/query.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(process.cwd(), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('fabcar'); - - // Evaluate the specified transaction. - // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') - // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') - const result = await contract.evaluateTransaction('queryAllCars'); - console.log(`Transaction has been evaluated, result is: ${result.toString()}`); - - } catch (error) { - console.error(`Failed to evaluate transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/fabcar/typescript/src/registerUser.ts b/fabcar/typescript/src/registerUser.ts deleted file mode 100644 index 45c07d3f..00000000 --- a/fabcar/typescript/src/registerUser.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Wallets, X509Identity } from 'fabric-network'; -import * as FabricCAServices from 'fabric-ca-client'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); - let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; - const ca = new FabricCAServices(caURL); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(process.cwd(), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const userIdentity = await wallet.get('appUser'); - if (userIdentity) { - console.log('An identity for the user "appUser" already exists in the wallet'); - return; - } - - // Check to see if we've already enrolled the admin user. - const adminIdentity = await wallet.get('admin'); - if (!adminIdentity) { - console.log('An identity for the admin user "admin" does not exist in the wallet'); - console.log('Run the enrollAdmin.ts application before retrying'); - return; - } - - // build a user object for authenticating with the CA - const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); - const adminUser = await provider.getUserContext(adminIdentity, 'admin'); - - // Register the user, enroll the user, and import the new identity into the wallet. - const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); - const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('appUser', x509Identity); - console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to register user "appUser": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/fabcar/typescript/tsconfig.json b/fabcar/typescript/tsconfig.json deleted file mode 100644 index 8c96ea07..00000000 --- a/fabcar/typescript/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "es2017", - "moduleResolution": "node", - "module": "commonjs", - "declaration": true, - "sourceMap": true - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./src/**/*.spec.ts" - ] -} diff --git a/fabcar/typescript/tslint.json b/fabcar/typescript/tslint.json deleted file mode 100644 index e08fd0b8..00000000 --- a/fabcar/typescript/tslint.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [true, "spaces", 4], - "linebreak-style": [true, "LF"], - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "no-console": false, - "curly": true, - "triple-equals": true, - "no-string-throw": true, - "no-var-keyword": true, - "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"], - "max-line-length": false - }, - "rulesDirectory": [] -} diff --git a/fabcar/typescript/wallet/.gitkeep b/fabcar/typescript/wallet/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test-network/network.sh b/test-network/network.sh index 6a3eb33b..68b5ff43 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -41,7 +41,7 @@ function printHelp() { echo " Used with "$'\e[0;32m'network.sh deployCC$'\e[0m' echo " -c - deploy chaincode to channel" echo " -ccn - the short name of the chaincode to deploy: basic (default),ledger, private, secured" - echo " -ccl - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" + echo " -ccl - the programming language of the chaincode to deploy: go (default), java, and javascript" echo " -ccv - chaincode version. 1.0 (default)" echo " -ccs - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc" echo " -ccp - Optional, path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index daa434c6..9ef8fe7f 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -62,8 +62,6 @@ if [ "$CC_SRC_PATH" = "NA" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-java/" elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-javascript/" - elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then - CC_SRC_PATH="$CC_SRC_PATH/chaincode-typescript/" fi # check that the language is available for the sample chaincode @@ -101,19 +99,9 @@ elif [ "$CC_SRC_LANGUAGE" = "java" ]; then elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_RUNTIME_LANGUAGE=node -elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then - CC_RUNTIME_LANGUAGE=node - - echo Compiling TypeScript code into JavaScript ... - pushd $CC_SRC_PATH - npm install - npm run build - popd - echo Finished compiling TypeScript code into JavaScript - else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, javascript, and typescript + echo Supported chaincode languages are: go, java, and javascript exit 1 fi From 7771b4f2161074f23dbdb2d4076a141791dd757c Mon Sep 17 00:00:00 2001 From: Arnaud J Le Hors Date: Thu, 23 Jul 2020 17:06:57 +0200 Subject: [PATCH 14/45] Revert "Remove Typescript References (#258)" (#261) This reverts commit 7a23d7872a700352b99161fb0edd60e59cc866d7. --- README.md | 2 +- .../application-typescript/.gitignore | 17 ++ .../application-typescript/package.json | 59 +++++++ .../application-typescript/src/enrollAdmin.ts | 56 +++++++ .../application-typescript/src/invoke.ts | 59 +++++++ .../application-typescript/src/query.ts | 57 +++++++ .../src/registerUser.ts | 68 ++++++++ .../application-typescript/tsconfig.json | 16 ++ .../application-typescript/tslint.json | 22 +++ .../chaincode-typescript/.gitignore | 16 ++ .../chaincode-typescript/package.json | 62 +++++++ .../chaincode-typescript/src/asset.ts | 12 ++ .../chaincode-typescript/src/assetTransfer.ts | 153 ++++++++++++++++++ .../chaincode-typescript/src/index.ts | 8 + .../chaincode-typescript/tsconfig.json | 16 ++ .../chaincode-typescript/tslint.json | 21 +++ chaincode/README.md | 2 +- chaincode/abstore/javascript/.gitignore | 3 + chaincode/fabcar/javascript/.gitignore | 3 + chaincode/fabcar/typescript/.editorconfig | 16 ++ chaincode/fabcar/typescript/.gitignore | 81 ++++++++++ chaincode/fabcar/typescript/package.json | 62 +++++++ chaincode/fabcar/typescript/src/car.ts | 11 ++ chaincode/fabcar/typescript/src/fabcar.ts | 140 ++++++++++++++++ chaincode/fabcar/typescript/src/index.ts | 8 + chaincode/fabcar/typescript/tsconfig.json | 16 ++ chaincode/fabcar/typescript/tslint.json | 21 +++ chaincode/marbles02/javascript/.gitignore | 3 + ci/azure-pipelines.yml | 8 + .../fabcar/azure-pipelines-typescript.yml | 22 +++ ci/templates/test-network/azure-pipelines.yml | 1 + .../organization/digibank/contract/.npmignore | 3 + .../magnetocorp/contract/.npmignore | 3 + fabcar/javascript/.gitignore | 3 + fabcar/networkDown.sh | 1 + fabcar/startFabric.sh | 30 +++- fabcar/typescript/.editorconfig | 16 ++ fabcar/typescript/.gitignore | 83 ++++++++++ fabcar/typescript/package.json | 59 +++++++ fabcar/typescript/src/enrollAdmin.ts | 52 ++++++ fabcar/typescript/src/invoke.ts | 53 ++++++ fabcar/typescript/src/query.ts | 51 ++++++ fabcar/typescript/src/registerUser.ts | 64 ++++++++ fabcar/typescript/tsconfig.json | 16 ++ fabcar/typescript/tslint.json | 22 +++ fabcar/typescript/wallet/.gitkeep | 0 test-network/network.sh | 2 +- test-network/scripts/deployCC.sh | 14 +- 48 files changed, 1508 insertions(+), 5 deletions(-) create mode 100644 asset-transfer-basic/application-typescript/.gitignore create mode 100644 asset-transfer-basic/application-typescript/package.json create mode 100644 asset-transfer-basic/application-typescript/src/enrollAdmin.ts create mode 100644 asset-transfer-basic/application-typescript/src/invoke.ts create mode 100644 asset-transfer-basic/application-typescript/src/query.ts create mode 100644 asset-transfer-basic/application-typescript/src/registerUser.ts create mode 100644 asset-transfer-basic/application-typescript/tsconfig.json create mode 100644 asset-transfer-basic/application-typescript/tslint.json create mode 100644 asset-transfer-basic/chaincode-typescript/.gitignore create mode 100644 asset-transfer-basic/chaincode-typescript/package.json create mode 100644 asset-transfer-basic/chaincode-typescript/src/asset.ts create mode 100644 asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts create mode 100644 asset-transfer-basic/chaincode-typescript/src/index.ts create mode 100644 asset-transfer-basic/chaincode-typescript/tsconfig.json create mode 100644 asset-transfer-basic/chaincode-typescript/tslint.json create mode 100755 chaincode/fabcar/typescript/.editorconfig create mode 100644 chaincode/fabcar/typescript/.gitignore create mode 100644 chaincode/fabcar/typescript/package.json create mode 100644 chaincode/fabcar/typescript/src/car.ts create mode 100644 chaincode/fabcar/typescript/src/fabcar.ts create mode 100644 chaincode/fabcar/typescript/src/index.ts create mode 100644 chaincode/fabcar/typescript/tsconfig.json create mode 100644 chaincode/fabcar/typescript/tslint.json create mode 100644 ci/templates/fabcar/azure-pipelines-typescript.yml create mode 100755 fabcar/typescript/.editorconfig create mode 100644 fabcar/typescript/.gitignore create mode 100644 fabcar/typescript/package.json create mode 100644 fabcar/typescript/src/enrollAdmin.ts create mode 100644 fabcar/typescript/src/invoke.ts create mode 100644 fabcar/typescript/src/query.ts create mode 100644 fabcar/typescript/src/registerUser.ts create mode 100644 fabcar/typescript/tsconfig.json create mode 100644 fabcar/typescript/tslint.json create mode 100644 fabcar/typescript/wallet/.gitkeep diff --git a/README.md b/README.md index c86caa91..9c0877f9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The asset transfer series provides a series of smart contracts and applications | **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. | **Coming soon** | Go, JavaScript | JavaScript | +| [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. | **Coming soon** | Go, JavaScript, Typescript | JavaScript, Typescript | | [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates how to deploy an index with your chaincode and issue rich queries when you are using CouchDB as your state database. | **Coming soon** | Go | **Coming soon** | | [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections and how the private data hash can be used to verify an agreement before executing a transfer | **Coming soon** | Go | **Coming soon** | | [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_private_asset_transfer_tutorial.html) | Go | **Coming soon** | diff --git a/asset-transfer-basic/application-typescript/.gitignore b/asset-transfer-basic/application-typescript/.gitignore new file mode 100644 index 00000000..e610e389 --- /dev/null +++ b/asset-transfer-basic/application-typescript/.gitignore @@ -0,0 +1,17 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# Compiled TypeScript files +dist + +wallet +!wallet/.gitkeep diff --git a/asset-transfer-basic/application-typescript/package.json b/asset-transfer-basic/application-typescript/package.json new file mode 100644 index 00000000..03546d28 --- /dev/null +++ b/asset-transfer-basic/application-typescript/package.json @@ -0,0 +1,59 @@ +{ + "name": "asset-transfer-basic", + "version": "1.0.0", + "description": "Asset-Transfer-Basic application implemented in TypeScript", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.1.0", + "fabric-network": "^2.1.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "^3.1.6" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts new file mode 100644 index 00000000..43076c06 --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as FabricCAServices from 'fabric-ca-client'; +import { Wallets, X509Identity } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get('admin'); + if (identity) { + console.log('An identity for the admin user "admin" already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('admin', x509Identity); + console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user "admin": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/invoke.ts b/asset-transfer-basic/application-typescript/src/invoke.ts new file mode 100644 index 00000000..205f3212 --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/invoke.ts @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('basic'); + + // Submit the specified transaction. (several example transactions are listed below) + // createAsset creates an asset with ID asset1, color yellow, owner Dave, size 5 and appraizedValue of 130 requires 6 arguments. + // ex: ('createAsset', 'asset1', 'yellow', 'Dave', 5, 1300) + // transferAsset transfers an asset with ID asset1 to new owner Tom - requires 2 arguments. + // ex: ('transferAsset', 'asset1', 'Tom') + await contract.submitTransaction('createAsset', 'asset13', 'yellow', "5", 'Tom', "1300"); + console.log(`Transaction has been submitted`); + + // Disconnect from the gateway. + await gateway.disconnect(); + + } catch (error) { + console.error(`Failed to submit transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/query.ts b/asset-transfer-basic/application-typescript/src/query.ts new file mode 100644 index 00000000..aae2243f --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/query.ts @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('basic'); + + // Evaluate the specified transaction. (several example transactions are listed below) + // getAsset returns an asset with given assetID asset4 - requires 1 argument. + // ex: ('getAsset', 'asset4') + // getAllAssets returns all assets in the world state - requires no arguments. + // ex: ('getAllAssets') + const result = await contract.evaluateTransaction('getAllAssets'); + console.log(`Transaction has been evaluated, result is: ${result.toString()}`); + + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/registerUser.ts b/asset-transfer-basic/application-typescript/src/registerUser.ts new file mode 100644 index 00000000..0b6b7a2c --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/registerUser.ts @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Wallets, X509Identity } from 'fabric-network'; +import * as FabricCAServices from 'fabric-ca-client'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; + const ca = new FabricCAServices(caURL); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userIdentity = await wallet.get('appUser'); + if (userIdentity) { + console.log('An identity for the user "appUser" already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminIdentity = await wallet.get('admin'); + if (!adminIdentity) { + console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('Run the enrollAdmin.ts application before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, 'admin'); + + // Register the user, enroll the user, and import the new identity into the wallet. + const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); + const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('appUser', x509Identity); + console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user "appUser": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/tsconfig.json b/asset-transfer-basic/application-typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/asset-transfer-basic/application-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/asset-transfer-basic/application-typescript/tslint.json b/asset-transfer-basic/application-typescript/tslint.json new file mode 100644 index 00000000..e08fd0b8 --- /dev/null +++ b/asset-transfer-basic/application-typescript/tslint.json @@ -0,0 +1,22 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"], + "max-line-length": false + }, + "rulesDirectory": [] +} diff --git a/asset-transfer-basic/chaincode-typescript/.gitignore b/asset-transfer-basic/chaincode-typescript/.gitignore new file mode 100644 index 00000000..79bfe1a3 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/.gitignore @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# Compiled TypeScript files +dist + diff --git a/asset-transfer-basic/chaincode-typescript/package.json b/asset-transfer-basic/chaincode-typescript/package.json new file mode 100644 index 00000000..2b681fc1 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/package.json @@ -0,0 +1,62 @@ +{ + "name": "asset-transfer-basic", + "version": "1.0.0", + "description": "Asset Transfer Basic contract implemented in TypeScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "start": "fabric-chaincode-node start", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.0.0", + "fabric-shim": "^2.0.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "^3.1.6" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/asset-transfer-basic/chaincode-typescript/src/asset.ts b/asset-transfer-basic/chaincode-typescript/src/asset.ts new file mode 100644 index 00000000..56d33439 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/asset.ts @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +export class Asset { + public docType?: string; + public ID: string; + public Color: string; + public Size: number; + public Owner: string; + public AppraisedValue: number; +} diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts new file mode 100644 index 00000000..04ebcb99 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -0,0 +1,153 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Context, Contract } from 'fabric-contract-api'; +import { Asset } from './asset'; + +export class AssetTransfer extends Contract { + + public async initLedger(ctx: Context) { + const assets: Asset[] = [ + { + ID: "asset1", + Color: "blue", + Size: 5, + Owner: "Tomoko", + AppraisedValue: 300, + }, + { + ID: "asset2", + Color: "red", + Size: 5, + Owner: "Brad", + AppraisedValue: 400, + }, + { + ID: "asset3", + Color: "green", + Size: 10, + Owner: "Jin Soo", + AppraisedValue: 500, + }, + { + ID: "asset4", + Color: "yellow", + Size: 10, + Owner: "Max", + AppraisedValue: 600, + }, + { + ID: "asset5", + Color: "black", + Size: 15, + Owner: "Adriana", + AppraisedValue: 700, + }, + { + ID: "asset6", + Color: "white", + Size: 15, + Owner: "Michel", + AppraisedValue: 800, + }, + ]; + + for (let i = 0; i < assets.length; i++) { + assets[i].docType = 'asset'; + await ctx.stub.putState(assets[i].ID, Buffer.from(JSON.stringify(assets[i]))); + console.info('Added <--> ', assets[i]); + } + } + + // createAsset issues a new asset to the world state with given details. + public async createAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + const asset = { + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + }; + + await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + } + + // readAsset returns the asset stored in the world state with given id. + public async readAsset(ctx: Context, id: string): Promise { + const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state + if (!assetJSON || assetJSON.length === 0) { + throw new Error(`The asset ${id} does not exist`); + } + + return assetJSON.toString(); + } + + // updateAsset updates an existing asset in the world state with provided parameters. + public async updateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + const exists = await this.assetExists(ctx, id); + if (!exists) { + throw new Error(`The asset ${id} does not exist`); + } + + // overwritting original asset with new asset + let updatedAsset = { + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + }; + + return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); + } + + // deleteAsset deletes an given asset from the world state. + public async deleteAsset(ctx: Context, id: string) { + const exists = await this.assetExists(ctx, id); + if (!exists) { + throw new Error(`The asset ${id} does not exist`); + } + + return ctx.stub.deleteState(id); + } + + // assetExists returns true when asset with given ID exists in world state. + public async assetExists(ctx: Context, id: string): Promise { + const assetJSON = await ctx.stub.getState(id); + if (!assetJSON || assetJSON.length === 0) { + return false; + } + return true; + } + + // transferAsset updates the owner field of asset with given id in the world state. + public async transferAsset(ctx: Context, id: string, newOwner: string) { + let assetString = await this.readAsset(ctx, id); + + let asset = JSON.parse(assetString); + asset.Owner = newOwner; + + await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + } + + // getAllAssets returns all assets found in the world state. + public async getAllAssets(ctx: Context): Promise { + const allResults = []; + // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. + for await (const { key, value } of ctx.stub.getStateByRange("", "")) { + const strValue = Buffer.from(value).toString('utf8'); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push({ Key: key, Record: record }); + } + console.info(allResults); + return JSON.stringify(allResults); + } + +} diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts new file mode 100644 index 00000000..a5f5e17b --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AssetTransfer } from './assetTransfer'; +export { AssetTransfer } from './assetTransfer'; + +export const contracts: any[] = [AssetTransfer]; diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/asset-transfer-basic/chaincode-typescript/tslint.json b/asset-transfer-basic/chaincode-typescript/tslint.json new file mode 100644 index 00000000..33ccbf3c --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/tslint.json @@ -0,0 +1,21 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"] + }, + "rulesDirectory": [] +} diff --git a/chaincode/README.md b/chaincode/README.md index 603de32c..ceeb0aab 100644 --- a/chaincode/README.md +++ b/chaincode/README.md @@ -6,7 +6,7 @@ This folder contains example smart contracts that are used by the Hyperledger Fa | **Smart Contract** | **Description** | **Tutorial** | **Languages** | | -----------|------------------------------|----------|---------| -| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript | +| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript, Typescript | | [marbles02](marbles02) | Sample that demonstrates how to deploy an index and use rich queries when you are using CouchDB as your state database. | [Using CouchDB](https://hyperledger-fabric.readthedocs.io/en/master/couchdb_tutorial.html) | Go | | [marbles02_private](marbles02_private) | Sample that demonstrates the use of private data collections. | [Private data tutorial](https://hyperledger-fabric.readthedocs.io/en/master/private_data_tutorial.html) | Go | | [marbles_transfer](marbles_transfer) | Smart contract that demonstrates the use of private data, state based endorsement, and access control to securely transfer an asset between two parties | [Marbles private asset transfer scenario](marbles_transfer/README.md) | Go | diff --git a/chaincode/abstore/javascript/.gitignore b/chaincode/abstore/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/abstore/javascript/.gitignore +++ b/chaincode/abstore/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/chaincode/fabcar/javascript/.gitignore b/chaincode/fabcar/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/fabcar/javascript/.gitignore +++ b/chaincode/fabcar/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/chaincode/fabcar/typescript/.editorconfig b/chaincode/fabcar/typescript/.editorconfig new file mode 100755 index 00000000..75a13be2 --- /dev/null +++ b/chaincode/fabcar/typescript/.editorconfig @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/chaincode/fabcar/typescript/.gitignore b/chaincode/fabcar/typescript/.gitignore new file mode 100644 index 00000000..69d6a33b --- /dev/null +++ b/chaincode/fabcar/typescript/.gitignore @@ -0,0 +1,81 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Compiled TypeScript files +dist + diff --git a/chaincode/fabcar/typescript/package.json b/chaincode/fabcar/typescript/package.json new file mode 100644 index 00000000..7cd3837c --- /dev/null +++ b/chaincode/fabcar/typescript/package.json @@ -0,0 +1,62 @@ +{ + "name": "fabcar", + "version": "1.0.0", + "description": "FabCar contract implemented in TypeScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "start": "fabric-chaincode-node start", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.0.0", + "fabric-shim": "^2.0.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "^3.1.6" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/chaincode/fabcar/typescript/src/car.ts b/chaincode/fabcar/typescript/src/car.ts new file mode 100644 index 00000000..ba101625 --- /dev/null +++ b/chaincode/fabcar/typescript/src/car.ts @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +export class Car { + public docType?: string; + public color: string; + public make: string; + public model: string; + public owner: string; +} diff --git a/chaincode/fabcar/typescript/src/fabcar.ts b/chaincode/fabcar/typescript/src/fabcar.ts new file mode 100644 index 00000000..597da7e2 --- /dev/null +++ b/chaincode/fabcar/typescript/src/fabcar.ts @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Context, Contract } from 'fabric-contract-api'; +import { Car } from './car'; + +export class FabCar extends Contract { + + public async initLedger(ctx: Context) { + console.info('============= START : Initialize Ledger ==========='); + const cars: Car[] = [ + { + color: 'blue', + make: 'Toyota', + model: 'Prius', + owner: 'Tomoko', + }, + { + color: 'red', + make: 'Ford', + model: 'Mustang', + owner: 'Brad', + }, + { + color: 'green', + make: 'Hyundai', + model: 'Tucson', + owner: 'Jin Soo', + }, + { + color: 'yellow', + make: 'Volkswagen', + model: 'Passat', + owner: 'Max', + }, + { + color: 'black', + make: 'Tesla', + model: 'S', + owner: 'Adriana', + }, + { + color: 'purple', + make: 'Peugeot', + model: '205', + owner: 'Michel', + }, + { + color: 'white', + make: 'Chery', + model: 'S22L', + owner: 'Aarav', + }, + { + color: 'violet', + make: 'Fiat', + model: 'Punto', + owner: 'Pari', + }, + { + color: 'indigo', + make: 'Tata', + model: 'Nano', + owner: 'Valeria', + }, + { + color: 'brown', + make: 'Holden', + model: 'Barina', + owner: 'Shotaro', + }, + ]; + + for (let i = 0; i < cars.length; i++) { + cars[i].docType = 'car'; + await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i]))); + console.info('Added <--> ', cars[i]); + } + console.info('============= END : Initialize Ledger ==========='); + } + + public async queryCar(ctx: Context, carNumber: string): Promise { + const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state + if (!carAsBytes || carAsBytes.length === 0) { + throw new Error(`${carNumber} does not exist`); + } + console.log(carAsBytes.toString()); + return carAsBytes.toString(); + } + + public async createCar(ctx: Context, carNumber: string, make: string, model: string, color: string, owner: string) { + console.info('============= START : Create Car ==========='); + + const car: Car = { + color, + docType: 'car', + make, + model, + owner, + }; + + await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); + console.info('============= END : Create Car ==========='); + } + + public async queryAllCars(ctx: Context): Promise { + const startKey = ''; + const endKey = ''; + const allResults = []; + for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) { + const strValue = Buffer.from(value).toString('utf8'); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push({ Key: key, Record: record }); + } + console.info(allResults); + return JSON.stringify(allResults); + } + + public async changeCarOwner(ctx: Context, carNumber: string, newOwner: string) { + console.info('============= START : changeCarOwner ==========='); + + const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state + if (!carAsBytes || carAsBytes.length === 0) { + throw new Error(`${carNumber} does not exist`); + } + const car: Car = JSON.parse(carAsBytes.toString()); + car.owner = newOwner; + + await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); + console.info('============= END : changeCarOwner ==========='); + } + +} diff --git a/chaincode/fabcar/typescript/src/index.ts b/chaincode/fabcar/typescript/src/index.ts new file mode 100644 index 00000000..c0a2fcf6 --- /dev/null +++ b/chaincode/fabcar/typescript/src/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FabCar } from './fabcar'; +export { FabCar } from './fabcar'; + +export const contracts: any[] = [ FabCar ]; diff --git a/chaincode/fabcar/typescript/tsconfig.json b/chaincode/fabcar/typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/chaincode/fabcar/typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/chaincode/fabcar/typescript/tslint.json b/chaincode/fabcar/typescript/tslint.json new file mode 100644 index 00000000..33ccbf3c --- /dev/null +++ b/chaincode/fabcar/typescript/tslint.json @@ -0,0 +1,21 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"] + }, + "rulesDirectory": [] +} diff --git a/chaincode/marbles02/javascript/.gitignore b/chaincode/marbles02/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/marbles02/javascript/.gitignore +++ b/chaincode/marbles02/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 93bcee56..dc336a0e 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -60,6 +60,14 @@ jobs: - template: templates/install-deps.yml - template: templates/fabcar/azure-pipelines-javascript.yml + - job: Fabcar_TypeScript + displayName: FabCar (TypeScript) + pool: + vmImage: ubuntu-18.04 + steps: + - template: templates/install-deps.yml + - template: templates/fabcar/azure-pipelines-typescript.yml + - job: TestNetwork displayName: Test Network pool: diff --git a/ci/templates/fabcar/azure-pipelines-typescript.yml b/ci/templates/fabcar/azure-pipelines-typescript.yml new file mode 100644 index 00000000..4e566f5b --- /dev/null +++ b/ci/templates/fabcar/azure-pipelines-typescript.yml @@ -0,0 +1,22 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +steps: + - script: ./startFabric.sh typescript + workingDirectory: fabcar + displayName: Start Fabric + - script: retry -- npm install + workingDirectory: fabcar/typescript + displayName: Install FabCar Application Dependencies + - script: npm run build + workingDirectory: fabcar/typescript + displayName: Build FabCar application + - script: | + set -ex + node dist/enrollAdmin + node dist/registerUser + node dist/invoke + node dist/query + workingDirectory: fabcar/typescript + displayName: Run FabCar Application diff --git a/ci/templates/test-network/azure-pipelines.yml b/ci/templates/test-network/azure-pipelines.yml index f2c28cda..e934455c 100644 --- a/ci/templates/test-network/azure-pipelines.yml +++ b/ci/templates/test-network/azure-pipelines.yml @@ -7,6 +7,7 @@ steps: ./network.sh up createChannel -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml ./network.sh deployCC -ccn basic -ccv 1 -ccl javascript -cci initLedger ./network.sh deployCC -ccn basic -ccv 2 -ccl golang -cci initLedger + ./network.sh deployCC -ccn basic -ccv 3 -ccl typescript -cci initLedger ./network.sh deployCC -ccn secure -ccv 1 -ccl golang ./network.sh down workingDirectory: test-network diff --git a/commercial-paper/organization/digibank/contract/.npmignore b/commercial-paper/organization/digibank/contract/.npmignore index 9f444c23..a00ca941 100644 --- a/commercial-paper/organization/digibank/contract/.npmignore +++ b/commercial-paper/organization/digibank/contract/.npmignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/commercial-paper/organization/magnetocorp/contract/.npmignore b/commercial-paper/organization/magnetocorp/contract/.npmignore index 9f444c23..a00ca941 100644 --- a/commercial-paper/organization/magnetocorp/contract/.npmignore +++ b/commercial-paper/organization/magnetocorp/contract/.npmignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/fabcar/javascript/.gitignore b/fabcar/javascript/.gitignore index a610a823..1dcaf1f2 100644 --- a/fabcar/javascript/.gitignore +++ b/fabcar/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/fabcar/networkDown.sh b/fabcar/networkDown.sh index 74765e96..c1bc2313 100755 --- a/fabcar/networkDown.sh +++ b/fabcar/networkDown.sh @@ -15,3 +15,4 @@ popd # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* +rm -rf typescript/wallet/* diff --git a/fabcar/startFabric.sh b/fabcar/startFabric.sh index 88edaed6..8ce18cb3 100755 --- a/fabcar/startFabric.sh +++ b/fabcar/startFabric.sh @@ -19,15 +19,18 @@ elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="../chaincode/fabcar/javascript/" elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_SRC_PATH="../chaincode/fabcar/java" +elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="../chaincode/fabcar/typescript/" else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, and javascript + echo Supported chaincode languages are: go, java, javascript, and typescript exit 1 fi # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* +rm -rf typescript/wallet/* rm -rf go/wallet/* # launch network; create channel and join peer to channel @@ -67,6 +70,31 @@ JavaScript: return all cars, but you can update the application to evaluate other transactions: node query +TypeScript: + + Start by changing into the "typescript" directory: + cd typescript + + Next, install all required packages: + npm install + + Next, compile the TypeScript code into JavaScript: + npm run build + + Then run the following applications to enroll the admin user, and register a new user + called appUser which will be used by the other applications to interact with the deployed + FabCar contract: + node dist/enrollAdmin + node dist/registerUser + + You can run the invoke application as follows. By default, the invoke application will + create a new car, but you can update the application to submit other transactions: + node dist/invoke + + You can run the query application as follows. By default, the query application will + return all cars, but you can update the application to evaluate other transactions: + node dist/query + Java: Start by changing into the "java" directory: diff --git a/fabcar/typescript/.editorconfig b/fabcar/typescript/.editorconfig new file mode 100755 index 00000000..75a13be2 --- /dev/null +++ b/fabcar/typescript/.editorconfig @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/fabcar/typescript/.gitignore b/fabcar/typescript/.gitignore new file mode 100644 index 00000000..2a6c904c --- /dev/null +++ b/fabcar/typescript/.gitignore @@ -0,0 +1,83 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Compiled TypeScript files +dist + +wallet +!wallet/.gitkeep diff --git a/fabcar/typescript/package.json b/fabcar/typescript/package.json new file mode 100644 index 00000000..6cb67aa1 --- /dev/null +++ b/fabcar/typescript/package.json @@ -0,0 +1,59 @@ +{ + "name": "fabcar", + "version": "1.0.0", + "description": "FabCar application implemented in TypeScript", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.1.0", + "fabric-network": "^2.1.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "^3.1.6" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/fabcar/typescript/src/enrollAdmin.ts b/fabcar/typescript/src/enrollAdmin.ts new file mode 100644 index 00000000..e84139b8 --- /dev/null +++ b/fabcar/typescript/src/enrollAdmin.ts @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as FabricCAServices from 'fabric-ca-client'; +import { Wallets, X509Identity } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get('admin'); + if (identity) { + console.log('An identity for the admin user "admin" already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('admin', x509Identity); + console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user "admin": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/invoke.ts b/fabcar/typescript/src/invoke.ts new file mode 100644 index 00000000..762263ed --- /dev/null +++ b/fabcar/typescript/src/invoke.ts @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Submit the specified transaction. + // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') + // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR12', 'Dave') + await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom'); + console.log(`Transaction has been submitted`); + + // Disconnect from the gateway. + await gateway.disconnect(); + + } catch (error) { + console.error(`Failed to submit transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/query.ts b/fabcar/typescript/src/query.ts new file mode 100644 index 00000000..b86677ee --- /dev/null +++ b/fabcar/typescript/src/query.ts @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Evaluate the specified transaction. + // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') + // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') + const result = await contract.evaluateTransaction('queryAllCars'); + console.log(`Transaction has been evaluated, result is: ${result.toString()}`); + + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/registerUser.ts b/fabcar/typescript/src/registerUser.ts new file mode 100644 index 00000000..45c07d3f --- /dev/null +++ b/fabcar/typescript/src/registerUser.ts @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Wallets, X509Identity } from 'fabric-network'; +import * as FabricCAServices from 'fabric-ca-client'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; + const ca = new FabricCAServices(caURL); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userIdentity = await wallet.get('appUser'); + if (userIdentity) { + console.log('An identity for the user "appUser" already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminIdentity = await wallet.get('admin'); + if (!adminIdentity) { + console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('Run the enrollAdmin.ts application before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, 'admin'); + + // Register the user, enroll the user, and import the new identity into the wallet. + const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); + const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('appUser', x509Identity); + console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user "appUser": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/tsconfig.json b/fabcar/typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/fabcar/typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/fabcar/typescript/tslint.json b/fabcar/typescript/tslint.json new file mode 100644 index 00000000..e08fd0b8 --- /dev/null +++ b/fabcar/typescript/tslint.json @@ -0,0 +1,22 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"], + "max-line-length": false + }, + "rulesDirectory": [] +} diff --git a/fabcar/typescript/wallet/.gitkeep b/fabcar/typescript/wallet/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test-network/network.sh b/test-network/network.sh index 68b5ff43..6a3eb33b 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -41,7 +41,7 @@ function printHelp() { echo " Used with "$'\e[0;32m'network.sh deployCC$'\e[0m' echo " -c - deploy chaincode to channel" echo " -ccn - the short name of the chaincode to deploy: basic (default),ledger, private, secured" - echo " -ccl - the programming language of the chaincode to deploy: go (default), java, and javascript" + echo " -ccl - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" echo " -ccv - chaincode version. 1.0 (default)" echo " -ccs - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc" echo " -ccp - Optional, path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index 9ef8fe7f..daa434c6 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -62,6 +62,8 @@ if [ "$CC_SRC_PATH" = "NA" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-java/" elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-javascript/" + elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-typescript/" fi # check that the language is available for the sample chaincode @@ -99,9 +101,19 @@ elif [ "$CC_SRC_LANGUAGE" = "java" ]; then elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_RUNTIME_LANGUAGE=node +elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_RUNTIME_LANGUAGE=node + + echo Compiling TypeScript code into JavaScript ... + pushd $CC_SRC_PATH + npm install + npm run build + popd + echo Finished compiling TypeScript code into JavaScript + else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, and javascript + echo Supported chaincode languages are: go, java, javascript, and typescript exit 1 fi From 8c32a85f66170771adf2808e2e6e6dacfd9f8cf5 Mon Sep 17 00:00:00 2001 From: nikhil550 Date: Thu, 23 Jul 2020 14:17:50 -0400 Subject: [PATCH 15/45] Update private data chaincode to use test network script (#249) Signed-off-by: NIKHIL E GUPTA Co-authored-by: NIKHIL E GUPTA --- .../chaincode-go/README.md | 100 ++++-------------- 1 file changed, 22 insertions(+), 78 deletions(-) diff --git a/asset-transfer-private-data/chaincode-go/README.md b/asset-transfer-private-data/chaincode-go/README.md index af2912b0..e8cc25f3 100644 --- a/asset-transfer-private-data/chaincode-go/README.md +++ b/asset-transfer-private-data/chaincode-go/README.md @@ -40,81 +40,24 @@ The test network is deployed with two peer organizations. The `createChannel` fl ## Deploy the smart contract to the channel -You can use the following steps to deploy the smart contract to the channel. - -### Install and approve the chaincode as Org1 - -Set the following environment variables to operate the `peer` CLI as the Org1 admin: +You can use the test network script to deploy the private data smart contract to the channel that was just created. Deploy the smart contract to `mychannel` using the following command: ``` -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/Admin@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 +./network.sh deployCC -ccn private -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json ``` -Run the following command to package the private asset transfer chaincode: -``` -peer lifecycle chaincode package private_transfer.tar.gz --path ../asset-transfer-private-data/chaincode-go --lang golang --label private_transfer_1 -``` - -The command will create a chaincode package named `private_transfer.tar.gz`. We can now install this package on the Org1 peer: -``` -peer lifecycle chaincode install private_transfer.tar.gz -``` - -You will need the chaincode package ID in order to approve the chaincode definition. You can find the package ID by querying your peer: -``` -peer lifecycle chaincode queryinstalled -``` -Save the package ID as an environment variable. The package ID will not be the same for all users, so need to use the result that was returned by the previous command: -``` -export PACKAGE_ID=private_transfer_1:d195682c777705f76a4cdce903a1365dc93a9b5b6fb363eeac292e616cfb64d2 -``` -You can now approve the chaincode as Org1. This command includes a path to the collection definition file. -``` -peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name private_transfer --version 1 --package-id $PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --collections-config ../asset-transfer-private-data/chaincode-go/collections_config.json --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` - -Note we are approving a chaincode endorsement policy of `"OR('Org1MSP.peer','Org2MSP.peer')"`. This allows Org1 and Org2 to create an asset without receiving an endorsement from the other organization. - - -### Install and approve the chaincode as Org2 - -We can now install and approve the chaincode as Org2. Set the following environment variables to operate as the Org2 admin: -``` -export CORE_PEER_TLS_ENABLED=true -export CORE_PEER_LOCALMSPID="Org2MSP" -export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -export CORE_PEER_ADDRESS=localhost:9051 -``` - -Because the chaincode is already packaged on our local machine, we can go ahead and install the chaincode on the Org2 peer:` -``` -peer lifecycle chaincode install private_transfer.tar.gz -``` - -We can now approve the chaincode as the Org2 admin: -``` -peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name private_transfer --version 1 --package-id $PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --collections-config ../asset-transfer-private-data/chaincode-go/collections_config.json --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` - -### Commit the chaincode definition the channel - -Now that a majority (2 out of 2) of channel members have approved the chaincode definition, Org2 can commit the chaincode definition and deploy the chaincode to the channel: -``` -peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name private_transfer --version 1 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --collections-config ../asset-transfer-private-data/chaincode-go/collections_config.json --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 --signature-policy "OR('Org1MSP.peer','Org2MSP.peer')" -``` -We are now ready use the private asset transfer smart contract. +Note that we are using the `-ccep` flag to deploy the private data smart contract with a chaincode endorsement policy of `"OR('Org1MSP.peer','Org2MSP.peer')"`. This allows Org1 and Org2 to create an asset without receiving an endorsement from the other organization. The command also uses the `-cccg` flag to provide the path to the collection configuration file. ## Register identities The private data transfer smart contract supports ownership by individual identities that belong to the network. In our scenario, the owner of the asset will be a member of Org1, while the buyer will belong to Org2. To highlight the connection between the `GetClientIdentity().GetID()` API and the information within a users certificate, we will register new two new identities using the Org1 and Org2 CA, and then use the CA's to generate each identities certificate and private key. -First, we will use the Org1 CA to create the identity asset owner. Set the Fabric CA client home to the MSP of the Org1 CA admin (this identity was generated by the test network script): +First, we need to set the following environment variables to use the the Fabric CA client: +``` +export PATH=${PWD}/../bin:${PWD}:$PATH +export FABRIC_CFG_PATH=$PWD/../config/ +``` + +We will use the Org1 CA to create the identity asset owner. Set the Fabric CA client home to the MSP of the Org1 CA admin (this identity was generated by the test network script): ``` export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/ ``` @@ -159,6 +102,7 @@ cp ${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml ${PWD Now that we have created the identity of the asset owner, we can invoke the private data smart contract to create a new asset. Use the following environment variables to operate the `peer` CLI as the owner identity from Org1. ``` +export CORE_PEER_TLS_ENABLED=true export CORE_PEER_LOCALMSPID="Org1MSP" export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt @@ -172,7 +116,7 @@ export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset We can the invoke the smart contract to create the new asset: ``` -peer chaincode invoke -o localhost:7050 --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 private_transfer -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}" +peer chaincode invoke -o localhost:7050 --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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}" ``` The command above uses the transient data flag, `--transient`, to provide the asset details to the smart contract. Transient data is not part of the transaction read/write set, and as result is not stored on the channel ledger. @@ -181,7 +125,7 @@ Note that command above only targets the Org1 peer. The `CreateAsset` transactio We can read the main details of the asset that was created by using the `ReadAsset` function to query the `assetCollection` collection: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAsset","Args":["asset1"]}' ``` When successful, the command will return the following result: @@ -201,7 +145,7 @@ x509::CN=owner,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.exampl A member of Org1 can also read the private asset appraisal value that is stored in the `Org1MSPPrivateCollection` on the Org1 peer: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' ``` The query will return the value of the asset: ``` @@ -221,7 +165,7 @@ export CORE_PEER_ADDRESS=localhost:9051 Now that we are operating as a member of Org2, we can demonstrate that the asset appraisal is not stored on the Org2 peer: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}' ``` The buyer only finds that asset1 does exist in his collection: ``` @@ -230,22 +174,22 @@ Error: endorsement failure during invoke. response: status:500 message:"appraisa Nor is a member of Org2 able to read the Org1 private data collection: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' ``` By setting `"memberOnlyRead": true` in the collection configuration file, we specify that only members of of Org1 can read data from the collection. A member who tries to read the collection would only get the following response. ``` -Error: endorsement failure during query. response: status:500 message:"failed to read from asset details GET_STATE failed: transaction ID: 10d39a7d0b340455a19ca4198146702d68d884d41a0e60936f1599c1ddb9c99d: tx creator does not have read access permission on privatedata in chaincodeName:private_transfer collectionName: Org1MSPPrivateCollection" +Error: endorsement failure during query. response: status:500 message:"failed to read from asset details GET_STATE failed: transaction ID: 10d39a7d0b340455a19ca4198146702d68d884d41a0e60936f1599c1ddb9c99d: tx creator does not have read access permission on privatedata in chaincodeName:private collectionName: Org1MSPPrivateCollection" ``` To purchase the asset, the buyer needs to agree to the same value as the asset owner. The agreed value will be stored in the `Org2MSPDetailsCollection` collection on the Org2 peer. Run the following command to agree to the appraised value of 100: ``` export ASSET_VALUE=$(echo -n "{\"assetID\":\"asset1\",\"appraisedValue\":100}" | base64 | tr -d \\n) -peer chaincode invoke -o localhost:7050 --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 private_transfer -c '{"function":"AgreeToTransfer","Args":[]}' --transient "{\"asset_value\":\"$ASSET_VALUE\"}" +peer chaincode invoke -o localhost:7050 --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 private -c '{"function":"AgreeToTransfer","Args":[]}' --transient "{\"asset_value\":\"$ASSET_VALUE\"}" ``` The buyer can now query the value they agreed to in the Org2 private data collection: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}' ``` The invoke will return the following value: ``` @@ -270,11 +214,11 @@ export ASSET_OWNER=$(echo -n "{\"assetID\":\"asset1\",\"buyerMSP\":\"Org2MSP\"}" The owner of the asset needs to initiate the transfer. ``` -peer chaincode invoke -o localhost:7050 --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 private_transfer -c '{"function":"TransferAsset","Args":[]}' --transient "{\"asset_owner\":\"$ASSET_OWNER\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt +peer chaincode invoke -o localhost:7050 --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 private -c '{"function":"TransferAsset","Args":[]}' --transient "{\"asset_owner\":\"$ASSET_OWNER\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt ``` You can query `asset1` to see the results of the transfer. ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAsset","Args":["asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAsset","Args":["asset1"]}' ``` The results will show that the buyer identity now owns the asset: @@ -290,7 +234,7 @@ x509::CN=buyer,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org2.exampl You can also confirm that transfer removed the private details from the Org1 collection: ``` -peer chaincode query -o localhost:7050 --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 private_transfer -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' +peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' ``` Your query will return the following result: ``` From 971af50cdfdffac9b7645db244d1c57474709ffd Mon Sep 17 00:00:00 2001 From: nikhil550 Date: Thu, 23 Jul 2020 23:36:33 -0400 Subject: [PATCH 16/45] Fix bugs in deployCC script (#265) Signed-off-by: NIKHIL E GUPTA Co-authored-by: NIKHIL E GUPTA --- test-network/scripts/deployCC.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index daa434c6..071ea381 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -40,11 +40,11 @@ if [ "$CC_SRC_PATH" = "NA" ]; then if [ "$CC_NAME" = "basic" ]; then echo $'\e[0;32m'asset-transfer-basic$'\e[0m' chaincode CC_SRC_PATH="../asset-transfer-basic" - elif [ "$CC_NAME" = "secure" ]; then + elif [ "$CC_NAME" = "secured" ]; then echo $'\e[0;32m'asset-transfer-secured-agreeement$'\e[0m' chaincode CC_SRC_PATH="../asset-transfer-secured-agreement" elif [ "$CC_NAME" = "ledger" ]; then - echo $'\e[0;32m'asset-transfer-secured-agreeement$'\e[0m' chaincode + echo $'\e[0;32m'asset-transfer-ledger-agreeement$'\e[0m' chaincode CC_SRC_PATH="../asset-transfer-ledger-queries" elif [ "$CC_NAME" = "private" ]; then echo $'\e[0;32m'asset-transfer-private-data$'\e[0m' chaincode From fef45be9954eb038505dace7e7b301e96d8bb35f Mon Sep 17 00:00:00 2001 From: Chris Gabriel <33184046+denali49@users.noreply.github.com> Date: Sat, 25 Jul 2020 12:43:22 -0500 Subject: [PATCH 17/45] Remove images folder from fabric-samples. (#266) Signed-off-by: Chris Gabriel Remove images from fabric-samples and move to docs. Signed-off-by: Chris Gabriel --- .../chaincode-go/images/transfer_assets_1.png | Bin 202640 -> 0 bytes .../chaincode-go/images/transfer_assets_2.png | Bin 244253 -> 0 bytes .../chaincode-go/images/transfer_assets_3.png | Bin 244831 -> 0 bytes .../images/transfer_assets_images.pptx | Bin 52676 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_1.png delete mode 100644 asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_2.png delete mode 100644 asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_3.png delete mode 100644 asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_images.pptx diff --git a/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_1.png b/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_1.png deleted file mode 100644 index 387a0c2c821d548f11f685bca590323bd36ab582..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202640 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,DASX;6;yjl0IGy%_~0jLUN@$Y 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 xqftA}i=5hkuK3!eFu< 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 z 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 diff --git a/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_2.png b/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_2.png deleted file mode 100644 index b31100a4bec1ea84e6493fb41451a8aa86cff5a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244253 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,DASX;6;yjl0IGy%_~0jLUN@$Y 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

Gq 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 IUorFfKQ7IZ-v9sr diff --git a/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_3.png b/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_3.png deleted file mode 100644 index 633173fc347a7fa5248d832c60cc0c6c1b34a2b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244831 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,DASX;6;yjl0IGy%_~0jLUN@$Y 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

MQUEvh!~)@Jaca5mok*I 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Ȳ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 diff --git a/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_images.pptx b/asset-transfer-secured-agreement/chaincode-go/images/transfer_assets_images.pptx deleted file mode 100644 index ac80a33db613aca90f5ef0f9d509960eb96347ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52676 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 &tob=s*#LZ9}lBvC| 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 From af81e1287f8c51a162ca74cf93c296cf054a2ed0 Mon Sep 17 00:00:00 2001 From: r2roC Date: Thu, 9 Jul 2020 15:43:13 -0400 Subject: [PATCH 18/45] Javascript version of asset-transfer-ledger-chaincode.go Tested using the test network Adapted from marbles02 Signed-off-by: r2roC --- .gitignore | 73 +++ .../chaincode-javascript/index.js | 13 + .../statedb/couchdb/indexes/indexOwner.json | 1 + .../lib/asset_transfer_ledger_chaincode.js | 425 ++++++++++++++++++ .../chaincode-javascript/package.json | 19 + 5 files changed, 531 insertions(+) create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/index.js create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js create mode 100644 asset-transfer-ledger-queries/chaincode-javascript/package.json diff --git a/.gitignore b/.gitignore index cf8cb35a..e5d12af8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,76 @@ vendor/ .vscode .gradle .idea +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless diff --git a/asset-transfer-ledger-queries/chaincode-javascript/index.js b/asset-transfer-ledger-queries/chaincode-javascript/index.js new file mode 100644 index 00000000..c4c35ee9 --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/index.js @@ -0,0 +1,13 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const CC = require('./lib/asset_transfer_ledger_chaincode.js'); + +module.exports.CC = CC; +module.exports.contracts = [ CC ]; \ No newline at end of file diff --git a/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json b/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json new file mode 100644 index 00000000..305f0904 --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} 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 new file mode 100644 index 00000000..6779f72c --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js @@ -0,0 +1,425 @@ +/* + SPDX-License-Identifier: Apache-2.0 +*/ + +// ====CHAINCODE EXECUTION SAMPLES (CLI) ================== + +// ==== Invoke assets ==== +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset1","blue","tom","35","100"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset2","red","tom","50","150"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset3","blue","tom","70","200"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAsset","asset2","jerry"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAssetsBasedOnColor","blue","jerry"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["deleteAsset","asset1"]}' + +// ==== Query assets ==== +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["readAsset","asset1"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["getAssetsByRange","asset1","asset3"]}' output issue go +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["getAssetHistory","asset1"]}' + +// Rich Query (Only supported if CouchDB is used as state database): +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssetsByOwner","tom"]}' output issue +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' output issue go + +// Rich Query with Pagination (Only supported if CouchDB is used as state database): +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' //error: invalid bookmark + +// INDEXES TO SUPPORT COUCHDB RICH QUERIES +// +// Indexes in CouchDB are required in order to make JSON queries efficient and are required for +// any JSON query with a sort. Indexes may be packaged alongside +// chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own +// text file with extension *.json with the index definition formatted in JSON following the +// CouchDB index JSON syntax as documented at: +// http://docs.couchdb.org/en/2.3.1/api/database/find.html#db-index +// +// This asset transfer ledger example chaincode demonstrates a packaged +// index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json. +// +// If you have access to the your peer's CouchDB state database in a development environment, +// you may want to iteratively test various indexes in support of your chaincode queries. You +// can use the CouchDB Fauxton interface or a command line curl utility to create and update +// indexes. Then once you finalize an index, include the index definition alongside your +// chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment +// to managed environments. +// +// In the examples below you can find index definitions that support asset transfer ledger +// chaincode queries, along with the syntax that you can use in development environments +// to create the indexes in the CouchDB Fauxton interface or a curl command line utility. +// + +// Index for docType, owner. +// +// Example curl command line to define index in the CouchDB channel_chaincode database +// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index +// + +// Index for docType, owner, size (descending order). +// +// Example curl command line to define index in the CouchDB channel_chaincode database +// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index + +// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' + +// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' + +'use strict'; + +const { Contract } = require('fabric-contract-api'); + +class Chaincode extends Contract{ + + // CreateAsset - create a new asset, store into chaincode state + async createAsset(ctx, assetID, color, owner, size, appraisedValue) { + const exists = await this.assetExists(ctx, assetID) + if (exists) { + throw new Error('asset 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; + + // === 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')); + } + + // 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(`The asset ${id} does not exist`); + } + + return assetJSON.toString(); + } + + // delete - remove a asset key/value pair from state + async deleteAsset(ctx, id) { + if (!id) { + throw new Error('asset name must not be empty'); + } + + var exists = await this.assetExists(ctx, id) + if (!exists) { + throw new Error('') + } + + // 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 + + // 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); + } + + // TransferAsset transfers a asset by setting a new owner name on the asset + async transferAsset(ctx, assetName, newOwner) { + + let assetAsBytes = await ctx.stub.getState(assetName); + if (!assetAsBytes || !assetAsBytes.toString()) { + throw new Error('asset 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 + + let assetJSONasBytes = Buffer.from(JSON.stringify(assetToTransfer)); + await ctx.stub.putState(assetName, assetJSONasBytes); //rewrite the asset + } + +// 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 resultsIterator = await ctx.stub.getStateByRange(startKey, endKey); + let results = await this.getAllResults(resultsIterator, false); + + return JSON.stringify(results); + } + + // 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 transferAssetsBasedOnColor(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]); + + // 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; + } + + let objectType; + let attributes; + ({ + objectType, + attributes + } = await ctx.stub.splitCompositeKey(responseRange.value.key)); + + let returnedColor = attributes[0]; + let returnedAssetName = attributes[1]; + + // Now call the transfer function for the found asset. + // Re-use the same function that is used to transfer individual assets + let response = await this.transferAsset(ctx, returnedAssetName, newOwner); + } + } + + // 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); + } + + // 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; + } + + // getQueryResultForQueryString executes the passed in query string. + // Result set is built and returned as a byte array containing the JSON results. + async getQueryResultForQueryString(ctx, queryString) { + + let resultsIterator = await ctx.stub.getQueryResult(queryString); + let results = await this.getAllResults(resultsIterator, false); + + return JSON.stringify(results); + } + + // 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) { + + const { iterator, metadata } = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark); + const results = await this.getAllResults(iterator, false); + + results.ResponseMetadata = { + RecordsCount: metadata.fetched_records_count, + Bookmark: metadata.bookmark, + }; + return JSON.stringify(results); + } + + // 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) { + + const { iterator, metadata } = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark); + const results = await this.getAllResults(iterator, false); + + results.ResponseMetadata = { + RecordsCount: metadata.fetched_records_count, + Bookmark: metadata.bookmark, + }; + + return JSON.stringify(results); + } + + // GetAssetHistory returns the chain of custody for an asset since issuance. + async getAssetHistory(ctx, assetName) { + + let resultsIterator = await ctx.stub.getHistoryForKey(assetName); + let results = await this.getAllResults(resultsIterator, true); + + return JSON.stringify(results); + } + + // 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); + if ( !assetState || assetState.length === 0 ) { + return false; + } + return true + } + + // getAllAssets returns all assets found in the world state. + async getAllAssets(ctx) { + const allResults = []; + // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. + for await (const { key, value } of ctx.stub.getStateByRange("", "")) { + const strValue = Buffer.from(value).toString('utf8'); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push({ Key: key, Record: record }); + } + return JSON.stringify(allResults); + } + + async getAllResults(iterator, isHistory) { + let allResults = []; + while (true) { + let res = await iterator.next(); + + if (res.value && res.value.value.toString()) { + let jsonRes = {}; + console.log(res.value.value.toString('utf8')); + if (isHistory && isHistory === true) { + jsonRes.TxId = res.value.tx_id; + jsonRes.Timestamp = res.value.timestamp; + try { + jsonRes.Value = JSON.parse(res.value.value.toString('utf8')); + } catch (err) { + console.log(err); + jsonRes.Value = res.value.value.toString('utf8'); + } + } else { + jsonRes.Key = res.value.key; + try { + jsonRes.Record = JSON.parse(res.value.value.toString('utf8')); + } catch (err) { + console.log(err); + jsonRes.Record = res.value.value.toString('utf8'); + } + } + allResults.push(jsonRes); + } + if (res.done) { + 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].owner, + assets[i].size, + assets[i].appraisedValue + ); + } + } +} + +module.exports = Chaincode; \ No newline at end of file diff --git a/asset-transfer-ledger-queries/chaincode-javascript/package.json b/asset-transfer-ledger-queries/chaincode-javascript/package.json new file mode 100644 index 00000000..24991cd0 --- /dev/null +++ b/asset-transfer-ledger-queries/chaincode-javascript/package.json @@ -0,0 +1,19 @@ +{ + "name": "asset-transfer-ledger-queries", + "version": "1.0.0", + "description": "asset chaincode implemented in node.js", + "main": "index.js", + "engines": { + "node": ">=8.4.0", + "npm": ">=5.3.0" + }, + "scripts": { + "start": "fabric-chaincode-node start" + }, + "engine-strict": true, + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.0.0", + "fabric-shim": "^2.0.0" + } +} From 666e61ec6e03b03c857672ad1c2249a55af5302f Mon Sep 17 00:00:00 2001 From: r2roC Date: Thu, 9 Jul 2020 16:13:46 -0400 Subject: [PATCH 19/45] fixed a couple unused variable issues Signed-off-by: r2roC --- .../lib/asset_transfer_ledger_chaincode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6779f72c..7b1b6b20 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 @@ -208,12 +208,12 @@ class Chaincode extends Contract{ attributes } = await ctx.stub.splitCompositeKey(responseRange.value.key)); - let returnedColor = attributes[0]; + console.log(objectType) let returnedAssetName = attributes[1]; // Now call the transfer function for the found asset. // Re-use the same function that is used to transfer individual assets - let response = await this.transferAsset(ctx, returnedAssetName, newOwner); + await this.transferAsset(ctx, returnedAssetName, newOwner); } } From 1c5cf4383c9b9d1b864bbdca2550872001d05724 Mon Sep 17 00:00:00 2001 From: r2roC Date: Fri, 10 Jul 2020 09:34:17 -0400 Subject: [PATCH 20/45] Added new lines at the bottom of index.js and asset_transfer_ledger_chaincode.js. Modified node version in package.json. Modified error messages in the chaincode to all begin uppercase. Signed-off-by: r2roC --- .../chaincode-javascript/index.js | 2 +- .../lib/asset_transfer_ledger_chaincode.js | 14 +++++++------- .../chaincode-javascript/package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/asset-transfer-ledger-queries/chaincode-javascript/index.js b/asset-transfer-ledger-queries/chaincode-javascript/index.js index c4c35ee9..5c7b6e0d 100644 --- a/asset-transfer-ledger-queries/chaincode-javascript/index.js +++ b/asset-transfer-ledger-queries/chaincode-javascript/index.js @@ -10,4 +10,4 @@ const CC = require('./lib/asset_transfer_ledger_chaincode.js'); module.exports.CC = CC; -module.exports.contracts = [ CC ]; \ No newline at end of file +module.exports.contracts = [ CC ]; 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 7b1b6b20..df458738 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 @@ -75,7 +75,7 @@ class Chaincode extends Contract{ async createAsset(ctx, assetID, color, owner, size, appraisedValue) { const exists = await this.assetExists(ctx, assetID) if (exists) { - throw new Error('asset exists') + throw new Error(`The asset ${assetID} already exists`) } // ==== Create asset object and marshal to JSON ==== @@ -101,7 +101,7 @@ class Chaincode extends Contract{ 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(`The asset ${id} does not exist`); + throw new Error(`Asset ${id} does not exist`); } return assetJSON.toString(); @@ -110,19 +110,19 @@ class Chaincode extends Contract{ // delete - remove a asset key/value pair from state async deleteAsset(ctx, id) { if (!id) { - throw new Error('asset name must not be empty'); + throw new Error('Asset name must not be empty'); } var exists = await this.assetExists(ctx, id) if (!exists) { - throw new Error('') + throw new Error(`Asset ${id} does not exist`) } // 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; + jsonResp.error = 'Asset does not exist: ' + name; throw new Error(jsonResp); } let assetJSON = {}; @@ -150,7 +150,7 @@ class Chaincode extends Contract{ let assetAsBytes = await ctx.stub.getState(assetName); if (!assetAsBytes || !assetAsBytes.toString()) { - throw new Error('asset does not exist'); + throw new Error(`Asset ${assetName} does not exist`); } let assetToTransfer = {}; try { @@ -422,4 +422,4 @@ class Chaincode extends Contract{ } } -module.exports = Chaincode; \ No newline at end of file +module.exports = Chaincode; diff --git a/asset-transfer-ledger-queries/chaincode-javascript/package.json b/asset-transfer-ledger-queries/chaincode-javascript/package.json index 24991cd0..0427b5c8 100644 --- a/asset-transfer-ledger-queries/chaincode-javascript/package.json +++ b/asset-transfer-ledger-queries/chaincode-javascript/package.json @@ -4,7 +4,7 @@ "description": "asset chaincode implemented in node.js", "main": "index.js", "engines": { - "node": ">=8.4.0", + "node": ">=12", "npm": ">=5.3.0" }, "scripts": { From 9a9a3d26834e540909c33a3c1e67b0aaef073200 Mon Sep 17 00:00:00 2001 From: r2roC Date: Fri, 10 Jul 2020 09:46:21 -0400 Subject: [PATCH 21/45] Modfied .gitignore so that node_modules is the only addition to the root .gitignore Signed-off-by: r2roC --- .gitignore | 71 ------------------------------------------------------ 1 file changed, 71 deletions(-) diff --git a/.gitignore b/.gitignore index e5d12af8..6ab3f7a5 100644 --- a/.gitignore +++ b/.gitignore @@ -14,76 +14,5 @@ vendor/ .vscode .gradle .idea -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - # Dependency directories node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless From c2b6fcfd3ff8ed3c24bb70d68816c7b040d57979 Mon Sep 17 00:00:00 2001 From: r2roC Date: Mon, 13 Jul 2020 09:32:04 -0400 Subject: [PATCH 22/45] Standardized createAsset parameters, removed getAllAssets function, and changed transferAssetsBasedOnColor to match go-chaincode Signed-off-by: r2roC --- .../lib/asset_transfer_ledger_chaincode.js | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) 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 df458738..c525a7e9 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 @@ -5,9 +5,9 @@ // ====CHAINCODE EXECUTION SAMPLES (CLI) ================== // ==== Invoke assets ==== -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset1","blue","tom","35","100"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset2","red","tom","50","150"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset3","blue","tom","70","200"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset1","blue","35","tom","100"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset2","red","50","tom","150"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset3","blue","70","tom","200"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAsset","asset2","jerry"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAssetsBasedOnColor","blue","jerry"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["deleteAsset","asset1"]}' @@ -72,7 +72,7 @@ const { Contract } = require('fabric-contract-api'); class Chaincode extends Contract{ // CreateAsset - create a new asset, store into chaincode state - async createAsset(ctx, assetID, color, owner, size, appraisedValue) { + 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`) @@ -81,7 +81,7 @@ class Chaincode extends Contract{ // ==== Create asset object and marshal to JSON ==== let asset = {}; asset.docType = 'asset'; - asset.ID = assetID; + asset.assetID = assetID; asset.color = color; asset.size = size; asset.owner = owner; @@ -90,7 +90,7 @@ class Chaincode extends Contract{ // === Save asset to state === await ctx.stub.putState(assetID, Buffer.from(JSON.stringify(asset))); let indexName = 'color~name' - let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.ID]); + let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.assetID]); // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value @@ -137,7 +137,7 @@ class Chaincode extends Contract{ // delete the index let indexName = 'color~name'; - let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.ID]); + let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.assetID]); if (!colorNameIndexKey) { throw new Error(' Failed to create the createCompositeKey'); } @@ -189,7 +189,7 @@ class Chaincode extends Contract{ // 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 transferAssetsBasedOnColor(ctx, color, newOwner) { + 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]); @@ -309,24 +309,6 @@ class Chaincode extends Contract{ return true } - // getAllAssets returns all assets found in the world state. - async getAllAssets(ctx) { - const allResults = []; - // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for await (const { key, value } of ctx.stub.getStateByRange("", "")) { - const strValue = Buffer.from(value).toString('utf8'); - let record; - try { - record = JSON.parse(strValue); - } catch (err) { - console.log(err); - record = strValue; - } - allResults.push({ Key: key, Record: record }); - } - return JSON.stringify(allResults); - } - async getAllResults(iterator, isHistory) { let allResults = []; while (true) { @@ -366,42 +348,42 @@ class Chaincode extends Contract{ async initLedger(ctx) { const assets = [ { - ID: 'asset1', + assetID: 'asset1', color: 'blue', size: 5, owner: 'Tom', appraisedValue: 100 }, { - ID: 'asset2', + assetID: 'asset2', color: 'red', size: 5, owner: 'Brad', appraisedValue: 100 }, { - ID: 'asset3', + assetID: 'asset3', color: 'green', size: 10, owner: 'Jin Soo', appraisedValue: 200 }, { - ID: 'asset4', + assetID: 'asset4', color: 'yellow', size: 10, owner: 'Max', appraisedValue: 200 }, { - ID: 'asset5', + assetID: 'asset5', color: 'black', size: 15, owner: 'Adriana', appraisedValue: 250 }, { - ID: 'asset6', + assetID: 'asset6', color: 'white', size: 15, owner: 'Michel', @@ -412,10 +394,10 @@ class Chaincode extends Contract{ for (let i = 0; i < assets.length; i++) { await this.createAsset( ctx, - assets[i].ID, + assets[i].assetID, assets[i].color, - assets[i].owner, assets[i].size, + assets[i].owner, assets[i].appraisedValue ); } From 64f280a1f3324a6691ec0516dcd7eb4582155cda Mon Sep 17 00:00:00 2001 From: r2roC Date: Wed, 22 Jul 2020 10:37:11 -0400 Subject: [PATCH 23/45] Update asset_transfer_ledger_chaincode.js Standardized function names Signed-off-by: r2roC --- .../lib/asset_transfer_ledger_chaincode.js | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) 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 c525a7e9..a3d80bd7 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 @@ -5,24 +5,24 @@ // ====CHAINCODE EXECUTION SAMPLES (CLI) ================== // ==== Invoke assets ==== -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset1","blue","35","tom","100"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset2","red","50","tom","150"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["createAsset","asset3","blue","70","tom","200"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAsset","asset2","jerry"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["transferAssetsBasedOnColor","blue","jerry"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["deleteAsset","asset1"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","35","tom","100"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","tom","150"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","tom","200"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAssetsBasedOnColor","blue","jerry"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}' // ==== Query assets ==== -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["readAsset","asset1"]}' -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["getAssetsByRange","asset1","asset3"]}' output issue go -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["getAssetHistory","asset1"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}' output issue go +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}' // Rich Query (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssetsByOwner","tom"]}' output issue -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' output issue go +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsByOwner","tom"]}' output issue +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' output issue go // Rich Query with Pagination (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' //error: invalid bookmark +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' //error: invalid bookmark // INDEXES TO SUPPORT COUCHDB RICH QUERIES // @@ -60,10 +60,10 @@ // curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' // Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["queryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' 'use strict'; @@ -72,8 +72,8 @@ const { Contract } = require('fabric-contract-api'); 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) + 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`) } @@ -97,8 +97,8 @@ class Chaincode extends Contract{ await ctx.stub.putState(colorNameIndexKey, Buffer.from('\u0000')); } - // readAsset returns the asset stored in the world state with given id. - async readAsset(ctx, id) { + // 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`); @@ -108,12 +108,12 @@ class Chaincode extends Contract{ } // delete - remove a asset key/value pair from state - async deleteAsset(ctx, id) { + async DeleteAsset(ctx, id) { if (!id) { throw new Error('Asset name must not be empty'); } - var exists = await this.assetExists(ctx, id) + var exists = await this.AssetExists(ctx, id) if (!exists) { throw new Error(`Asset ${id} does not exist`) } @@ -146,7 +146,7 @@ class Chaincode extends Contract{ } // TransferAsset transfers a asset by setting a new owner name on the asset - async transferAsset(ctx, assetName, newOwner) { + async TransferAsset(ctx, assetName, newOwner) { let assetAsBytes = await ctx.stub.getState(assetName); if (!assetAsBytes || !assetAsBytes.toString()) { @@ -174,10 +174,10 @@ class Chaincode extends Contract{ // 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) { + async GetAssetsByRange(ctx, startKey, endKey) { let resultsIterator = await ctx.stub.getStateByRange(startKey, endKey); - let results = await this.getAllResults(resultsIterator, false); + let results = await this.GetAllResults(resultsIterator, false); return JSON.stringify(results); } @@ -189,7 +189,7 @@ class Chaincode extends Contract{ // 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) { + 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]); @@ -213,7 +213,7 @@ class Chaincode extends Contract{ // 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); + await this.TransferAsset(ctx, returnedAssetName, newOwner); } } @@ -222,32 +222,32 @@ class Chaincode extends Contract{ // 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) { + 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)); + let queryResults = await this.GetQueryResultForQueryString(ctx, JSON.stringify(queryString)); return queryResults; //shim.success(queryResults); } // Example: Ad hoc rich query - // queryAssets uses a query string to perform a query for assets. + // 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. + // 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); + async QueryAssets(ctx, queryString) { + let queryResults = await this.GetQueryResultForQueryString(ctx, queryString); return queryResults; } - // getQueryResultForQueryString executes the passed in query string. + // 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) { + async GetQueryResultForQueryString(ctx, queryString) { let resultsIterator = await ctx.stub.getQueryResult(queryString); - let results = await this.getAllResults(resultsIterator, false); + let results = await this.GetAllResults(resultsIterator, false); return JSON.stringify(results); } @@ -257,10 +257,10 @@ class Chaincode extends Contract{ // 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) { + async GetAssetsByRangeWithPagination(ctx, startKey, endKey, pageSize, bookmark) { const { iterator, metadata } = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark); - const results = await this.getAllResults(iterator, false); + const results = await this.GetAllResults(iterator, false); results.ResponseMetadata = { RecordsCount: metadata.fetched_records_count, @@ -277,10 +277,10 @@ class Chaincode extends Contract{ // 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) { + async QueryAssetsWithPagination(ctx, queryString, pageSize, bookmark) { const { iterator, metadata } = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark); - const results = await this.getAllResults(iterator, false); + const results = await this.GetAllResults(iterator, false); results.ResponseMetadata = { RecordsCount: metadata.fetched_records_count, @@ -291,16 +291,16 @@ class Chaincode extends Contract{ } // GetAssetHistory returns the chain of custody for an asset since issuance. - async getAssetHistory(ctx, assetName) { + async GetAssetHistory(ctx, assetName) { let resultsIterator = await ctx.stub.getHistoryForKey(assetName); - let results = await this.getAllResults(resultsIterator, true); + let results = await this.GetAllResults(resultsIterator, true); return JSON.stringify(results); } // AssetExists returns true when asset with given ID exists in world state - async assetExists(ctx, assetName) { + async AssetExists(ctx, assetName) { // ==== Check if asset already exists ==== let assetState = await ctx.stub.getState(assetName); if ( !assetState || assetState.length === 0 ) { @@ -309,7 +309,7 @@ class Chaincode extends Contract{ return true } - async getAllResults(iterator, isHistory) { + async GetAllResults(iterator, isHistory) { let allResults = []; while (true) { let res = await iterator.next(); @@ -345,7 +345,7 @@ class Chaincode extends Contract{ } // InitLedger creates sample assets in the ledger - async initLedger(ctx) { + async InitLedger(ctx) { const assets = [ { assetID: 'asset1', @@ -392,7 +392,7 @@ class Chaincode extends Contract{ ]; for (let i = 0; i < assets.length; i++) { - await this.createAsset( + await this.CreateAsset( ctx, assets[i].assetID, assets[i].color, From d72be92feb7b877b32ac3a3798846d5ebc99a13d Mon Sep 17 00:00:00 2001 From: NIKHIL E GUPTA Date: Mon, 27 Jul 2020 12:33:10 -0400 Subject: [PATCH 24/45] private data readme edits Signed-off-by: NIKHIL E GUPTA --- .../chaincode-go/README.md | 15 ++++----------- test-network/scripts/deployCC.sh | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/asset-transfer-private-data/chaincode-go/README.md b/asset-transfer-private-data/chaincode-go/README.md index e8cc25f3..f2442a24 100644 --- a/asset-transfer-private-data/chaincode-go/README.md +++ b/asset-transfer-private-data/chaincode-go/README.md @@ -16,13 +16,6 @@ These three collections are used to transfer the asset between Org1 and Org2. In The private data asset transfer enabled by this smart contract is meant to demonstrate the use private data collections. For an example of a more realistic transfer scenario, see the [secure asset transfer smart contract](../../asset-transfer-secured-agreement/chaincode-go). -## Download the smart contract dependencies - -Before you install the smart contract on the network, you should download the smart contract dependencies. Run the following command from the `fabric-samples/asset-transfer-private-data/chaincode-go` directory. -``` -GO111MODULE=on go mod vendor -``` - ## Deploy the smart contract to the test network You can run the private data transfer scenario 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. @@ -49,7 +42,7 @@ Note that we are using the `-ccep` flag to deploy the private data smart contrac ## Register identities -The private data transfer smart contract supports ownership by individual identities that belong to the network. In our scenario, the owner of the asset will be a member of Org1, while the buyer will belong to Org2. To highlight the connection between the `GetClientIdentity().GetID()` API and the information within a users certificate, we will register new two new identities using the Org1 and Org2 CA, and then use the CA's to generate each identities certificate and private key. +The private data transfer smart contract supports ownership by individual identities that belong to the network. In our scenario, the owner of the asset will be a member of Org1, while the buyer will belong to Org2. To highlight the connection between the `GetClientIdentity().GetID()` API and the information within a user's certificate, we will register two new identities using the Org1 and Org2 Certificate Authorities (CA's), and then use the CA's to generate each identity's certificate and private key. First, we need to set the following environment variables to use the the Fabric CA client: ``` @@ -114,7 +107,7 @@ Run the following command to define the asset properties: export ASSET_PROPERTIES=$(echo -n "{\"objectType\":\"asset\",\"assetID\":\"asset1\",\"color\":\"green\",\"size\":20,\"appraisedValue\":100}" | base64 | tr -d \\n) ``` -We can the invoke the smart contract to create the new asset: +We can then invoke the smart contract to create the new asset: ``` peer chaincode invoke -o localhost:7050 --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 private -c '{"function":"CreateAsset","Args":[]}' --transient "{\"asset_properties\":\"$ASSET_PROPERTIES\"}" ``` @@ -167,7 +160,7 @@ Now that we are operating as a member of Org2, we can demonstrate that the asset ``` peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}' ``` -The buyer only finds that asset1 does exist in his collection: +The buyer only finds that asset1 does exist in the Org1 collection: ``` Error: endorsement failure during invoke. response: status:500 message:"appraisal value for asset1 does not exist in private data collection" ``` @@ -176,7 +169,7 @@ Nor is a member of Org2 able to read the Org1 private data collection: ``` peer chaincode query -o localhost:7050 --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 private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}' ``` -By setting `"memberOnlyRead": true` in the collection configuration file, we specify that only members of of Org1 can read data from the collection. A member who tries to read the collection would only get the following response. +By setting `"memberOnlyRead": true` in the collection configuration file, we specify that only members of Org1 can read data from the collection. A member who tries to read the collection would only get the following response. ``` Error: endorsement failure during query. response: status:500 message:"failed to read from asset details GET_STATE failed: transaction ID: 10d39a7d0b340455a19ca4198146702d68d884d41a0e60936f1599c1ddb9c99d: tx creator does not have read access permission on privatedata in chaincodeName:private collectionName: Org1MSPPrivateCollection" ``` diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index 071ea381..6f5ed46e 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -51,7 +51,7 @@ if [ "$CC_SRC_PATH" = "NA" ]; then CC_SRC_PATH="../asset-transfer-private-data" else echo The chaincode name ${CC_NAME} is not supported by this script - echo Supported chaincode names are: basic, secure, and private + echo Supported chaincode names are: basic, secured, and private exit 1 fi From 66b4b2b86a6c5a341e4e036b0a8a8c46cfe8392b Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 12:36:38 -0400 Subject: [PATCH 25/45] Remove Typescript Application As the typescript application shows no Fabric specific features we have elected to remove it Signed-off-by: Brett Logan --- .../application-typescript/.gitignore | 17 ----- .../application-typescript/package.json | 59 ---------------- .../application-typescript/src/enrollAdmin.ts | 56 --------------- .../application-typescript/src/invoke.ts | 59 ---------------- .../application-typescript/src/query.ts | 57 ---------------- .../src/registerUser.ts | 68 ------------------- .../application-typescript/tsconfig.json | 16 ----- .../application-typescript/tslint.json | 22 ------ 8 files changed, 354 deletions(-) delete mode 100644 asset-transfer-basic/application-typescript/.gitignore delete mode 100644 asset-transfer-basic/application-typescript/package.json delete mode 100644 asset-transfer-basic/application-typescript/src/enrollAdmin.ts delete mode 100644 asset-transfer-basic/application-typescript/src/invoke.ts delete mode 100644 asset-transfer-basic/application-typescript/src/query.ts delete mode 100644 asset-transfer-basic/application-typescript/src/registerUser.ts delete mode 100644 asset-transfer-basic/application-typescript/tsconfig.json delete mode 100644 asset-transfer-basic/application-typescript/tslint.json diff --git a/asset-transfer-basic/application-typescript/.gitignore b/asset-transfer-basic/application-typescript/.gitignore deleted file mode 100644 index e610e389..00000000 --- a/asset-transfer-basic/application-typescript/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -# Coverage directory used by tools like istanbul -coverage - -# Dependency directories -node_modules/ -jspm_packages/ -package-lock.json - -# Compiled TypeScript files -dist - -wallet -!wallet/.gitkeep diff --git a/asset-transfer-basic/application-typescript/package.json b/asset-transfer-basic/application-typescript/package.json deleted file mode 100644 index 03546d28..00000000 --- a/asset-transfer-basic/application-typescript/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "asset-transfer-basic", - "version": "1.0.0", - "description": "Asset-Transfer-Basic application implemented in TypeScript", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "scripts": { - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "pretest": "npm run lint", - "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", - "build": "tsc", - "build:watch": "tsc -w", - "prepublishOnly": "npm run build" - }, - "engineStrict": true, - "author": "Hyperledger", - "license": "Apache-2.0", - "dependencies": { - "fabric-ca-client": "^2.1.0", - "fabric-network": "^2.1.0" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.5", - "@types/node": "^10.12.10", - "@types/sinon": "^5.0.7", - "@types/sinon-chai": "^3.2.1", - "chai": "^4.2.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0", - "ts-node": "^7.0.1", - "tslint": "^5.11.0", - "typescript": "^3.1.6" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "coverage/**", - "dist/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 - } -} diff --git a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts deleted file mode 100644 index 43076c06..00000000 --- a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as FabricCAServices from 'fabric-ca-client'; -import { Wallets, X509Identity } from 'fabric-network'; -import * as fs from 'fs'; -import * as path from 'path'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; - const caTLSCACerts = caInfo.tlsCACerts.pem; - const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the admin user. - const identity = await wallet.get('admin'); - if (identity) { - console.log('An identity for the admin user "admin" already exists in the wallet'); - return; - } - - // Enroll the admin user, and import the new identity into the wallet. - const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('admin', x509Identity); - console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to enroll admin user "admin": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/invoke.ts b/asset-transfer-basic/application-typescript/src/invoke.ts deleted file mode 100644 index 205f3212..00000000 --- a/asset-transfer-basic/application-typescript/src/invoke.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Submit the specified transaction. (several example transactions are listed below) - // createAsset creates an asset with ID asset1, color yellow, owner Dave, size 5 and appraizedValue of 130 requires 6 arguments. - // ex: ('createAsset', 'asset1', 'yellow', 'Dave', 5, 1300) - // transferAsset transfers an asset with ID asset1 to new owner Tom - requires 2 arguments. - // ex: ('transferAsset', 'asset1', 'Tom') - await contract.submitTransaction('createAsset', 'asset13', 'yellow', "5", 'Tom', "1300"); - console.log(`Transaction has been submitted`); - - // Disconnect from the gateway. - await gateway.disconnect(); - - } catch (error) { - console.error(`Failed to submit transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/query.ts b/asset-transfer-basic/application-typescript/src/query.ts deleted file mode 100644 index aae2243f..00000000 --- a/asset-transfer-basic/application-typescript/src/query.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Gateway, Wallets } from 'fabric-network'; -import * as path from 'path'; -import * as fs from 'fs'; - - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const identity = await wallet.get('appUser'); - if (!identity) { - console.log('An identity for the user "appUser" does not exist in the wallet'); - console.log('Run the registerUser.ts application before retrying'); - return; - } - - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); - - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork('mychannel'); - - // Get the contract from the network. - const contract = network.getContract('basic'); - - // Evaluate the specified transaction. (several example transactions are listed below) - // getAsset returns an asset with given assetID asset4 - requires 1 argument. - // ex: ('getAsset', 'asset4') - // getAllAssets returns all assets in the world state - requires no arguments. - // ex: ('getAllAssets') - const result = await contract.evaluateTransaction('getAllAssets'); - console.log(`Transaction has been evaluated, result is: ${result.toString()}`); - - } catch (error) { - console.error(`Failed to evaluate transaction: ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/src/registerUser.ts b/asset-transfer-basic/application-typescript/src/registerUser.ts deleted file mode 100644 index 0b6b7a2c..00000000 --- a/asset-transfer-basic/application-typescript/src/registerUser.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Wallets, X509Identity } from 'fabric-network'; -import * as FabricCAServices from 'fabric-ca-client'; -import * as path from 'path'; -import * as fs from 'fs'; - -async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; - const ca = new FabricCAServices(caURL); - - // Create a new file system based wallet for managing identities. - const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); - - // Check to see if we've already enrolled the user. - const userIdentity = await wallet.get('appUser'); - if (userIdentity) { - console.log('An identity for the user "appUser" already exists in the wallet'); - return; - } - - // Check to see if we've already enrolled the admin user. - const adminIdentity = await wallet.get('admin'); - if (!adminIdentity) { - console.log('An identity for the admin user "admin" does not exist in the wallet'); - console.log('Run the enrollAdmin.ts application before retrying'); - return; - } - - // build a user object for authenticating with the CA - const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); - const adminUser = await provider.getUserContext(adminIdentity, 'admin'); - - // Register the user, enroll the user, and import the new identity into the wallet. - const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); - const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); - const x509Identity: X509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put('appUser', x509Identity); - console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to register user "appUser": ${error}`); - process.exit(1); - } -} - -main(); diff --git a/asset-transfer-basic/application-typescript/tsconfig.json b/asset-transfer-basic/application-typescript/tsconfig.json deleted file mode 100644 index 8c96ea07..00000000 --- a/asset-transfer-basic/application-typescript/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "es2017", - "moduleResolution": "node", - "module": "commonjs", - "declaration": true, - "sourceMap": true - }, - "include": [ - "./src/**/*" - ], - "exclude": [ - "./src/**/*.spec.ts" - ] -} diff --git a/asset-transfer-basic/application-typescript/tslint.json b/asset-transfer-basic/application-typescript/tslint.json deleted file mode 100644 index e08fd0b8..00000000 --- a/asset-transfer-basic/application-typescript/tslint.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "indent": [true, "spaces", 4], - "linebreak-style": [true, "LF"], - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "no-console": false, - "curly": true, - "triple-equals": true, - "no-string-throw": true, - "no-var-keyword": true, - "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"], - "max-line-length": false - }, - "rulesDirectory": [] -} From e31ecf316598efc7c6228d25a300b63a6d19ca16 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 12:37:10 -0400 Subject: [PATCH 26/45] Decorate Typescript Chaincode Add the contract-api specific annotations and enable the experimental features. Also properly format code according to the linter Signed-off-by: Brett Logan --- .../chaincode-typescript/src/asset.ts | 18 +++- .../chaincode-typescript/src/assetTransfer.ts | 82 +++++++++---------- .../chaincode-typescript/src/index.ts | 5 +- .../chaincode-typescript/tsconfig.json | 1 + .../chaincode-typescript/tslint.json | 4 +- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/asset-transfer-basic/chaincode-typescript/src/asset.ts b/asset-transfer-basic/chaincode-typescript/src/asset.ts index 56d33439..9fd62d35 100644 --- a/asset-transfer-basic/chaincode-typescript/src/asset.ts +++ b/asset-transfer-basic/chaincode-typescript/src/asset.ts @@ -1,12 +1,26 @@ /* - * SPDX-License-Identifier: Apache-2.0 - */ + SPDX-License-Identifier: Apache-2.0 +*/ +import {Object, Property} from 'fabric-contract-api'; + +@Object() export class Asset { + @Property() public docType?: string; + + @Property() public ID: string; + + @Property() public Color: string; + + @Property() public Size: number; + + @Property() public Owner: string; + + @Property() public AppraisedValue: number; } diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts index 04ebcb99..5646ce54 100644 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -2,65 +2,67 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Context, Contract } from 'fabric-contract-api'; -import { Asset } from './asset'; +import {Context, Contract, Returns, Transaction} from 'fabric-contract-api'; +import {Asset} from './asset'; export class AssetTransfer extends Contract { + @Transaction() public async initLedger(ctx: Context) { const assets: Asset[] = [ { - ID: "asset1", - Color: "blue", + ID: 'asset1', + Color: 'blue', Size: 5, - Owner: "Tomoko", + Owner: 'Tomoko', AppraisedValue: 300, }, { - ID: "asset2", - Color: "red", + ID: 'asset2', + Color: 'red', Size: 5, - Owner: "Brad", + Owner: 'Brad', AppraisedValue: 400, }, { - ID: "asset3", - Color: "green", + ID: 'asset3', + Color: 'green', Size: 10, - Owner: "Jin Soo", + Owner: 'Jin Soo', AppraisedValue: 500, }, { - ID: "asset4", - Color: "yellow", + ID: 'asset4', + Color: 'yellow', Size: 10, - Owner: "Max", + Owner: 'Max', AppraisedValue: 600, }, { - ID: "asset5", - Color: "black", + ID: 'asset5', + Color: 'black', Size: 15, - Owner: "Adriana", + Owner: 'Adriana', AppraisedValue: 700, }, { - ID: "asset6", - Color: "white", + ID: 'asset6', + Color: 'white', Size: 15, - Owner: "Michel", + Owner: 'Michel', AppraisedValue: 800, }, ]; - for (let i = 0; i < assets.length; i++) { - assets[i].docType = 'asset'; - await ctx.stub.putState(assets[i].ID, Buffer.from(JSON.stringify(assets[i]))); - console.info('Added <--> ', assets[i]); + for (const asset of assets) { + asset.docType = 'asset'; + await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); + console.info(`Asset ${asset.ID} initialized`); } } // createAsset issues a new asset to the world state with given details. + @Transaction() public async createAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { const asset = { ID: id, @@ -69,73 +71,72 @@ export class AssetTransfer extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } // readAsset returns the asset stored in the world state with given id. + @Transaction(false) public async readAsset(ctx: Context, id: string): Promise { const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state if (!assetJSON || assetJSON.length === 0) { throw new Error(`The asset ${id} does not exist`); } - return assetJSON.toString(); } // updateAsset updates an existing asset in the world state with provided parameters. + @Transaction() public async updateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { const exists = await this.assetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } - // overwritting original asset with new asset - let updatedAsset = { + // overwriting original asset with new asset + const updatedAsset = { ID: id, Color: color, Size: size, Owner: owner, AppraisedValue: appraisedValue, }; - return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); } // deleteAsset deletes an given asset from the world state. + @Transaction() public async deleteAsset(ctx: Context, id: string) { const exists = await this.assetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } - return ctx.stub.deleteState(id); } // assetExists returns true when asset with given ID exists in world state. + @Transaction(false) + @Returns('boolean') public async assetExists(ctx: Context, id: string): Promise { const assetJSON = await ctx.stub.getState(id); - if (!assetJSON || assetJSON.length === 0) { - return false; - } - return true; + return assetJSON && assetJSON.length > 0; } // transferAsset updates the owner field of asset with given id in the world state. + @Transaction() public async transferAsset(ctx: Context, id: string, newOwner: string) { - let assetString = await this.readAsset(ctx, id); - - let asset = JSON.parse(assetString); + const assetString = await this.readAsset(ctx, id); + const asset = JSON.parse(assetString); asset.Owner = newOwner; - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } // getAllAssets returns all assets found in the world state. + @Transaction(false) + @Returns('string') public async getAllAssets(ctx: Context): Promise { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for await (const { key, value } of ctx.stub.getStateByRange("", "")) { + for await (const {key, value} of ctx.stub.getStateByRange('', '')) { const strValue = Buffer.from(value).toString('utf8'); let record; try { @@ -144,9 +145,8 @@ export class AssetTransfer extends Contract { console.log(err); record = strValue; } - allResults.push({ Key: key, Record: record }); + allResults.push({Key: key, Record: record}); } - console.info(allResults); return JSON.stringify(allResults); } diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts index a5f5e17b..c018f4b7 100644 --- a/asset-transfer-basic/chaincode-typescript/src/index.ts +++ b/asset-transfer-basic/chaincode-typescript/src/index.ts @@ -2,7 +2,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AssetTransfer } from './assetTransfer'; -export { AssetTransfer } from './assetTransfer'; +import {AssetTransfer} from './assetTransfer'; + +export {AssetTransfer} from './assetTransfer'; export const contracts: any[] = [AssetTransfer]; diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json index 8c96ea07..2eaf5b98 100644 --- a/asset-transfer-basic/chaincode-typescript/tsconfig.json +++ b/asset-transfer-basic/chaincode-typescript/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "experimentalDecorators": true, "outDir": "dist", "target": "es2017", "moduleResolution": "node", diff --git a/asset-transfer-basic/chaincode-typescript/tslint.json b/asset-transfer-basic/chaincode-typescript/tslint.json index 33ccbf3c..a52c3ee2 100644 --- a/asset-transfer-basic/chaincode-typescript/tslint.json +++ b/asset-transfer-basic/chaincode-typescript/tslint.json @@ -15,7 +15,9 @@ "no-string-throw": true, "no-var-keyword": true, "no-trailing-whitespace": true, - "object-literal-key-quotes": [true, "as-needed"] + "object-literal-key-quotes": [true, "as-needed"], + "object-literal-sort-keys": false, + "max-line-length": false }, "rulesDirectory": [] } From 73a62a678011bebe4737159ed11e6ad8b57dee4e Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 12:51:34 -0400 Subject: [PATCH 27/45] Clean up Node Chaincode and Address Linting Issues Signed-off-by: Brett Logan --- .../chaincode-javascript/lib/assetTransfer.js | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js index f3f3fc77..7ce14c10 100644 --- a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js +++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js @@ -56,10 +56,10 @@ class AssetTransfer extends Contract { }, ]; - for (let i = 0; i < assets.length; i++) { - assets[i].docType = 'asset'; - await ctx.stub.putState(assets[i].ID, Buffer.from(JSON.stringify(assets[i]))); - console.info('Added <--> ', assets[i]); + for (const asset of assets) { + assets.docType = 'asset'; + await ctx.stub.putState(assets.ID, Buffer.from(JSON.stringify(assets))); + console.info(`Asset ${asset.ID} initialized`); } } @@ -72,8 +72,7 @@ class AssetTransfer extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } // readAsset returns the asset stored in the world state with given id. @@ -82,7 +81,6 @@ class AssetTransfer extends Contract { if (!assetJSON || assetJSON.length === 0) { throw new Error(`The asset ${id} does not exist`); } - return assetJSON.toString(); } @@ -93,15 +91,14 @@ class AssetTransfer extends Contract { throw new Error(`The asset ${id} does not exist`); } - // overwritting original asset with new asset - let updatedAsset = { + // overwriting original asset with new asset + const updatedAsset = { ID: id, Color: color, Size: size, Owner: owner, AppraisedValue: appraisedValue, }; - return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); } @@ -111,34 +108,28 @@ class AssetTransfer extends Contract { if (!exists) { throw new Error(`The asset ${id} does not exist`); } - return ctx.stub.deleteState(id); } // assetExists returns true when asset with given ID exists in world state. async assetExists(ctx, id) { const assetJSON = await ctx.stub.getState(id); - if (!assetJSON || assetJSON.length === 0) { - return false; - } - return true; + return assetJSON && assetJSON.length > 0; } // transferAsset updates the owner field of asset with given id in the world state. async transferAsset(ctx, id, newOwner) { - let assetString = await this.readAsset(ctx, id); - - let asset = JSON.parse(assetString); + const assetString = await this.readAsset(ctx, id); + const asset = JSON.parse(assetString); asset.Owner = newOwner; - - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } // getAllAssets returns all assets found in the world state. async getAllAssets(ctx) { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for await (const { key, value } of ctx.stub.getStateByRange("", "")) { + for (const { key, value } of ctx.stub.getStateByRange('', '')) { const strValue = Buffer.from(value).toString('utf8'); let record; try { @@ -149,7 +140,6 @@ class AssetTransfer extends Contract { } allResults.push({ Key: key, Record: record }); } - console.info(allResults); return JSON.stringify(allResults); } From 06c42bf68b0c070a9cfcb39aee4aea28d4d74a4e Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 12:59:13 -0400 Subject: [PATCH 28/45] Clean up Node Application and Address Linting Issues Signed-off-by: Brett Logan --- .../application-javascript/app.js | 40 ++++++++++--------- .../application-javascript/package.json | 4 +- .../application-javascript/registerUser.js | 4 +- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js index d6eeaa83..5961455b 100644 --- a/asset-transfer-basic/application-javascript/app.js +++ b/asset-transfer-basic/application-javascript/app.js @@ -6,17 +6,17 @@ 'use strict'; -const { Gateway, Wallets } = require('fabric-network'); +const {Gateway, Wallets} = require('fabric-network'); const path = require('path'); const fs = require('fs'); const registerUser = require('./registerUser'); const enrollAdmin = require('./enrollAdmin'); const myChannel = 'mychannel'; -const myChaincodeName = 'basic'; +const myChaincodeName = 'basic'; function prettyJSONString(inputString) { - return JSON.stringify(JSON.parse(inputString),null,2); + return JSON.stringify(JSON.parse(inputString), null, 2); } // pre-requisites: @@ -41,22 +41,26 @@ async function main() { // Steps: // Note: Steps 1 & 2 need to done only once in an app-server per blockchain network // 1. register & enroll admin user with CA, stores admin identity in local wallet - enrollAdmin.EnrollAdminUser(); + await enrollAdmin.EnrollAdminUser(); // 2. register & enroll application user with CA, which is used as client identify to make chaincode calls, stores app user identity in local wallet - registerUser.RegisterAppUser(); + await registerUser.RegisterAppUser(); // Check to see if app user exist in wallet. const identity = await wallet.get(registerUser.ApplicationUserId); if (!identity) { - console.log('An identity for the user does not exist in the wallet: '+ registerUser.ApplicationUserId); + console.log(`An identity for the user does not exist in the wallet: ${registerUser.ApplicationUserId}`); return; } //3. Prepare to call chaincode using fabric javascript node sdk // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); - await gateway.connect(ccp, { wallet, identity: registerUser.ApplicationUserId, discovery: { enabled: true, asLocalhost: true } }); + await gateway.connect(ccp, { + wallet, + identity: registerUser.ApplicationUserId, + discovery: {enabled: true, asLocalhost: true} + }); try { // Get the network (channel) our contract is deployed to. const network = await gateway.getNetwork(myChannel); @@ -72,40 +76,38 @@ async function main() { // GetAllAssets returns all the current assets on the ledger let result = await contract.evaluateTransaction('GetAllAssets'); - console.log('Evaluate Transaction: GetAllAssets, result: ' + prettyJSONString(result.toString()) ); + console.log(`Evaluate Transaction: GetAllAssets, result: ${prettyJSONString(result.toString())}`); console.log('\n***********************'); console.log('Submit Transaction: CreateAsset asset13'); //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraizedValue of 1300 - await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', 5, 'Tom', 1300); + await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); console.log('Evaluate Transaction: ReadAsset asset13'); // ReadAsset returns an asset with given assetID result = await contract.evaluateTransaction('ReadAsset', 'asset13'); - console.log(' result: ' + prettyJSONString(result.toString()) ); + console.log(` result: ${prettyJSONString(result.toString())}`); console.log('\n***********************'); console.log('Evaluate Transaction: AssetExists asset1'); // AssetExists returns 'true' if an asset with given assetID exist result = await contract.evaluateTransaction('AssetExists', 'asset1'); - console.log(' result: ' + prettyJSONString(result.toString()) ); + console.log(` result: ${prettyJSONString(result.toString())}`); console.log('Submit Transaction: UpdateAsset asset1, new AppraisedValue : 350'); // UpdateAsset updates an existing asset with new properties. Same args as CreateAsset - await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', 5, 'Tomoko', 350); + await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350'); console.log('Evaluate Transaction: ReadAsset asset1'); result = await contract.evaluateTransaction('ReadAsset', 'asset1'); - console.log(' result: ' + prettyJSONString(result.toString()) ); + console.log(` result: ${prettyJSONString(result.toString())}`); try { console.log('\nSubmit Transaction: UpdateAsset asset70'); //Non existing asset asset70 should throw Error - await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', 5, 'Tomoko', 300); - } - catch (error) { - let errMsg = 'Expected an error on UpdateAsset of non-existing Asset. '; - console.log(errMsg + error); + await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300'); + } catch (error) { + console.log(`Expected an error on UpdateAsset of non-existing Asset: ${error}`); } console.log('\n***********************'); @@ -115,7 +117,7 @@ async function main() { console.log('Evaluate Transaction: ReadAsset asset1'); result = await contract.evaluateTransaction('ReadAsset', 'asset1'); - console.log(' result: ' + prettyJSONString(result.toString()) ); + console.log(` result: ${prettyJSONString(result.toString())}`); } finally { // Disconnect from the gateway peer when all work for this client identity is complete diff --git a/asset-transfer-basic/application-javascript/package.json b/asset-transfer-basic/application-javascript/package.json index fd2e25c2..cc7d93ad 100644 --- a/asset-transfer-basic/application-javascript/package.json +++ b/asset-transfer-basic/application-javascript/package.json @@ -15,8 +15,8 @@ "author": "Hyperledger", "license": "Apache-2.0", "dependencies": { - "fabric-ca-client": "^2.1.0", - "fabric-network": "^2.1.0" + "fabric-ca-client": "2.2.0", + "fabric-network": "2.2.0" }, "devDependencies": { "chai": "^4.2.0", diff --git a/asset-transfer-basic/application-javascript/registerUser.js b/asset-transfer-basic/application-javascript/registerUser.js index 16c14535..d57e4118 100644 --- a/asset-transfer-basic/application-javascript/registerUser.js +++ b/asset-transfer-basic/application-javascript/registerUser.js @@ -35,7 +35,7 @@ async function registerAppUser() { // Check to see if we've already enrolled the user. const userIdentity = await wallet.get(applicationUserId); if (userIdentity) { - console.log('An identity for the user '+applicationUserId+' already exists in the wallet'); + console.log(`An identity for the user ${applicationUserId} already exists in the wallet`); return; } @@ -71,7 +71,7 @@ async function registerAppUser() { type: 'X.509', }; await wallet.put(applicationUserId, x509Identity); - console.log('Successfully registered and enrolled user '+applicationUserId +' and imported it into the wallet'); + console.log(`Successfully registered and enrolled user ${applicationUserId} and imported it into the wallet`); } catch (error) { console.error(`Failed to register user : ${error}`); From bd38f9220bbba049d7204833dfd36abf8f38cdb4 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 13:38:51 -0400 Subject: [PATCH 29/45] Capitalize Function Names Short of creating a second application that calls the lowerCase version of functions, since Go requires the functions be exported, capitalizing them in the Node and Typescript chaincode seems the simplest solution Signed-off-by: Brett Logan --- .../chaincode-javascript/lib/assetTransfer.js | 36 +++++++++---------- .../chaincode-typescript/src/assetTransfer.ts | 36 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js index 7ce14c10..f36eff34 100644 --- a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js +++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js @@ -10,7 +10,7 @@ const { Contract } = require('fabric-contract-api'); class AssetTransfer extends Contract { - async initLedger(ctx) { + async InitLedger(ctx) { const assets = [ { ID: 'asset1', @@ -63,8 +63,8 @@ class AssetTransfer extends Contract { } } - // createAsset issues a new asset to the world state with given details. - async createAsset(ctx, id, color, size, owner, appraisedValue) { + // CreateAsset issues a new asset to the world state with given details. + async CreateAsset(ctx, id, color, size, owner, appraisedValue) { const asset = { ID: id, Color: color, @@ -75,8 +75,8 @@ class AssetTransfer extends Contract { return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } - // readAsset returns the asset stored in the world state with given id. - async readAsset(ctx, id) { + // 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(`The asset ${id} does not exist`); @@ -84,9 +84,9 @@ class AssetTransfer extends Contract { return assetJSON.toString(); } - // updateAsset updates an existing asset in the world state with provided parameters. - async updateAsset(ctx, id, color, size, owner, appraisedValue) { - const exists = await this.assetExists(ctx, id); + // UpdateAsset updates an existing asset in the world state with provided parameters. + async UpdateAsset(ctx, id, color, size, owner, appraisedValue) { + const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } @@ -102,31 +102,31 @@ class AssetTransfer extends Contract { return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); } - // deleteAsset deletes an given asset from the world state. - async deleteAsset(ctx, id) { - const exists = await this.assetExists(ctx, id); + // DeleteAsset deletes an given asset from the world state. + async DeleteAsset(ctx, id) { + const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } return ctx.stub.deleteState(id); } - // assetExists returns true when asset with given ID exists in world state. - async assetExists(ctx, id) { + // AssetExists returns true when asset with given ID exists in world state. + async AssetExists(ctx, id) { const assetJSON = await ctx.stub.getState(id); return assetJSON && assetJSON.length > 0; } - // transferAsset updates the owner field of asset with given id in the world state. - async transferAsset(ctx, id, newOwner) { - const assetString = await this.readAsset(ctx, id); + // TransferAsset updates the owner field of asset with given id in the world state. + async TransferAsset(ctx, id, newOwner) { + const assetString = await this.ReadAsset(ctx, id); const asset = JSON.parse(assetString); asset.Owner = newOwner; return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } - // getAllAssets returns all assets found in the world state. - async getAllAssets(ctx) { + // GetAllAssets returns all assets found in the world state. + async GetAllAssets(ctx) { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. for (const { key, value } of ctx.stub.getStateByRange('', '')) { diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts index 5646ce54..0895685d 100644 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -8,7 +8,7 @@ import {Asset} from './asset'; export class AssetTransfer extends Contract { @Transaction() - public async initLedger(ctx: Context) { + public async InitLedger(ctx: Context) { const assets: Asset[] = [ { ID: 'asset1', @@ -61,9 +61,9 @@ export class AssetTransfer extends Contract { } } - // createAsset issues a new asset to the world state with given details. + // CreateAsset issues a new asset to the world state with given details. @Transaction() - public async createAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + public async CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { const asset = { ID: id, Color: color, @@ -74,9 +74,9 @@ export class AssetTransfer extends Contract { await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } - // readAsset returns the asset stored in the world state with given id. + // ReadAsset returns the asset stored in the world state with given id. @Transaction(false) - public async readAsset(ctx: Context, id: string): Promise { + public async ReadAsset(ctx: Context, id: string): Promise { const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state if (!assetJSON || assetJSON.length === 0) { throw new Error(`The asset ${id} does not exist`); @@ -84,10 +84,10 @@ export class AssetTransfer extends Contract { return assetJSON.toString(); } - // updateAsset updates an existing asset in the world state with provided parameters. + // UpdateAsset updates an existing asset in the world state with provided parameters. @Transaction() - public async updateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { - const exists = await this.assetExists(ctx, id); + public async UpdateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } @@ -103,37 +103,37 @@ export class AssetTransfer extends Contract { return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); } - // deleteAsset deletes an given asset from the world state. + // DeleteAsset deletes an given asset from the world state. @Transaction() - public async deleteAsset(ctx: Context, id: string) { - const exists = await this.assetExists(ctx, id); + public async DeleteAsset(ctx: Context, id: string) { + const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); } return ctx.stub.deleteState(id); } - // assetExists returns true when asset with given ID exists in world state. + // AssetExists returns true when asset with given ID exists in world state. @Transaction(false) @Returns('boolean') - public async assetExists(ctx: Context, id: string): Promise { + public async AssetExists(ctx: Context, id: string): Promise { const assetJSON = await ctx.stub.getState(id); return assetJSON && assetJSON.length > 0; } - // transferAsset updates the owner field of asset with given id in the world state. + // TransferAsset updates the owner field of asset with given id in the world state. @Transaction() - public async transferAsset(ctx: Context, id: string, newOwner: string) { - const assetString = await this.readAsset(ctx, id); + public async TransferAsset(ctx: Context, id: string, newOwner: string) { + const assetString = await this.ReadAsset(ctx, id); const asset = JSON.parse(assetString); asset.Owner = newOwner; await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); } - // getAllAssets returns all assets found in the world state. + // GetAllAssets returns all assets found in the world state. @Transaction(false) @Returns('string') - public async getAllAssets(ctx: Context): Promise { + public async GetAllAssets(ctx: Context): Promise { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. for await (const {key, value} of ctx.stub.getStateByRange('', '')) { From 4c3fe1731000178e7be2e45b4b6f1e6ea46d9590 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 15:14:27 -0400 Subject: [PATCH 30/45] Refactor GetAllAssets GetAllAssets uses getStateByRange which returns an iterator. Refactored code to make use of the iterator as this code did not function in its previous state Signed-off-by: Brett Logan --- .../chaincode-javascript/lib/assetTransfer.js | 13 ++++++---- .../chaincode-typescript/src/assetTransfer.ts | 24 +++++++++++-------- .../chaincode-typescript/src/index.ts | 6 ++--- .../chaincode-typescript/tsconfig.json | 1 + 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js index f36eff34..6927eca1 100644 --- a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js +++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js @@ -57,8 +57,8 @@ class AssetTransfer extends Contract { ]; for (const asset of assets) { - assets.docType = 'asset'; - await ctx.stub.putState(assets.ID, Buffer.from(JSON.stringify(assets))); + asset.docType = 'asset'; + await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); console.info(`Asset ${asset.ID} initialized`); } } @@ -129,8 +129,10 @@ class AssetTransfer extends Contract { async GetAllAssets(ctx) { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for (const { key, value } of ctx.stub.getStateByRange('', '')) { - const strValue = Buffer.from(value).toString('utf8'); + const iterator = await ctx.stub.getStateByRange('', ''); + let result = await iterator.next(); + while (!result.done) { + const strValue = Buffer.from(result.value.value.toString()).toString('utf8'); let record; try { record = JSON.parse(strValue); @@ -138,7 +140,8 @@ class AssetTransfer extends Contract { console.log(err); record = strValue; } - allResults.push({ Key: key, Record: record }); + allResults.push({ Key: result.value.key, Record: record }); + result = await iterator.next(); } return JSON.stringify(allResults); } diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts index 0895685d..b21f12ec 100644 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -2,13 +2,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Context, Contract, Returns, Transaction} from 'fabric-contract-api'; +import {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api'; import {Asset} from './asset'; -export class AssetTransfer extends Contract { +@Info({title: 'AssetTransfer', description: 'Smart contract for trading assets'}) +export class AssetTransferContract extends Contract { @Transaction() - public async InitLedger(ctx: Context) { + public async InitLedger(ctx: Context): Promise { const assets: Asset[] = [ { ID: 'asset1', @@ -63,7 +64,7 @@ export class AssetTransfer extends Contract { // CreateAsset issues a new asset to the world state with given details. @Transaction() - public async CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + public async CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise { const asset = { ID: id, Color: color, @@ -86,7 +87,7 @@ export class AssetTransfer extends Contract { // UpdateAsset updates an existing asset in the world state with provided parameters. @Transaction() - public async UpdateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + public async UpdateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise { const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); @@ -105,7 +106,7 @@ export class AssetTransfer extends Contract { // DeleteAsset deletes an given asset from the world state. @Transaction() - public async DeleteAsset(ctx: Context, id: string) { + public async DeleteAsset(ctx: Context, id: string): Promise { const exists = await this.AssetExists(ctx, id); if (!exists) { throw new Error(`The asset ${id} does not exist`); @@ -123,7 +124,7 @@ export class AssetTransfer extends Contract { // TransferAsset updates the owner field of asset with given id in the world state. @Transaction() - public async TransferAsset(ctx: Context, id: string, newOwner: string) { + public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise { const assetString = await this.ReadAsset(ctx, id); const asset = JSON.parse(assetString); asset.Owner = newOwner; @@ -136,8 +137,10 @@ export class AssetTransfer extends Contract { public async GetAllAssets(ctx: Context): Promise { const allResults = []; // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. - for await (const {key, value} of ctx.stub.getStateByRange('', '')) { - const strValue = Buffer.from(value).toString('utf8'); + const iterator = await ctx.stub.getStateByRange('', ''); + let result = await iterator.next(); + while (!result.done) { + const strValue = Buffer.from(result.value.value.toString()).toString('utf8'); let record; try { record = JSON.parse(strValue); @@ -145,7 +148,8 @@ export class AssetTransfer extends Contract { console.log(err); record = strValue; } - allResults.push({Key: key, Record: record}); + allResults.push({Key: result.value.key, Record: record}); + result = await iterator.next(); } return JSON.stringify(allResults); } diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts index c018f4b7..cdd7b585 100644 --- a/asset-transfer-basic/chaincode-typescript/src/index.ts +++ b/asset-transfer-basic/chaincode-typescript/src/index.ts @@ -2,8 +2,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {AssetTransfer} from './assetTransfer'; +import {AssetTransferContract} from './assetTransfer'; -export {AssetTransfer} from './assetTransfer'; +export {AssetTransferContract} from './assetTransfer'; -export const contracts: any[] = [AssetTransfer]; +export const contracts: any[] = [AssetTransferContract]; diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json index 2eaf5b98..80d8e12d 100644 --- a/asset-transfer-basic/chaincode-typescript/tsconfig.json +++ b/asset-transfer-basic/chaincode-typescript/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "experimentalDecorators": true, + "emitDecoratorMetadata": true, "outDir": "dist", "target": "es2017", "moduleResolution": "node", From e6184563adfbed195e500ff61e0834ecf27b713d Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Wed, 15 Jul 2020 14:14:31 -0400 Subject: [PATCH 31/45] Update CI For Asset Transfer Pushes the scripting logic into a standalone script and adds tests for the applications which by side-effect also test the invoke and query functions on the chaincode. Signed-off-by: Brett Logan --- ci/azure-pipelines.yml | 62 ++++++++++++++++++- ci/scripts/pullFabricImages.sh | 14 ++--- ci/scripts/run-test-network-basic.sh | 29 +++++++++ ci/scripts/run-test-network-ledger.sh | 21 +++++++ ci/scripts/run-test-network-private.sh | 21 +++++++ ci/scripts/run-test-network-secured.sh | 21 +++++++ ci/templates/test-network/azure-pipelines.yml | 14 ----- 7 files changed, 159 insertions(+), 23 deletions(-) create mode 100755 ci/scripts/run-test-network-basic.sh create mode 100755 ci/scripts/run-test-network-ledger.sh create mode 100755 ci/scripts/run-test-network-private.sh create mode 100755 ci/scripts/run-test-network-secured.sh delete mode 100644 ci/templates/test-network/azure-pipelines.yml diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index dc336a0e..926d6e6f 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -68,10 +68,68 @@ jobs: - template: templates/install-deps.yml - template: templates/fabcar/azure-pipelines-typescript.yml - - job: TestNetwork + - job: TestNetworkBasic displayName: Test Network pool: vmImage: ubuntu-18.04 + strategy: + matrix: + Basic-Go: + CHAINCODE_NAME: basic + CHAINCODE_LANGUAGE: go + Basic-Javascript: + CHAINCODE_NAME: basic + CHAINCODE_LANGUAGE: javascript + Basic-Typescript: + CHAINCODE_NAME: basic + CHAINCODE_LANGUAGE: typescript steps: - template: templates/install-deps.yml - - template: templates/test-network/azure-pipelines.yml + - script: ../ci/scripts/run-test-network-basic.sh + workingDirectory: test-network + displayName: Run Test Network Basic Chaincode + + - job: TestNetworkLedger + displayName: Test Network + pool: + vmImage: ubuntu-18.04 + strategy: + matrix: + Ledger-Go: + CHAINCODE_NAME: ledger + CHAINCODE_LANGUAGE: go + steps: + - template: templates/install-deps.yml + - script: ../ci/scripts/run-test-network-ledger.sh + workingDirectory: test-network + displayName: Run Test Network Ledger Chaincode + + - job: TestNetworkPrivate + displayName: Test Network + pool: + vmImage: ubuntu-18.04 + strategy: + matrix: + Private-Go: + CHAINCODE_NAME: private + CHAINCODE_LANGUAGE: go + steps: + - template: templates/install-deps.yml + - script: ../ci/scripts/run-test-network-private.sh + workingDirectory: test-network + displayName: Run Test Network Private Chaincode + + - job: TestNetworkSecured + displayName: Test Network + pool: + vmImage: ubuntu-18.04 + strategy: + matrix: + Secured-Go: + CHAINCODE_NAME: secured + CHAINCODE_LANGUAGE: go + steps: + - template: templates/install-deps.yml + - script: ../ci/scripts/run-test-network-secured.sh + workingDirectory: test-network + displayName: Run Test Network Secured Chaincode diff --git a/ci/scripts/pullFabricImages.sh b/ci/scripts/pullFabricImages.sh index eb15f87d..f58566b3 100755 --- a/ci/scripts/pullFabricImages.sh +++ b/ci/scripts/pullFabricImages.sh @@ -1,15 +1,15 @@ #!/bin/bash -e set -euo pipefail -# FABRIC_VERSION is set in ci/azure-pipelines.yml +FABRIC_VERSION=${FABRIC_VERSION:-2.2} STABLE_TAG=amd64-${FABRIC_VERSION}-stable -for image in baseos peer orderer ca tools orderer ccenv javaenv nodeenv; do - docker pull -q hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG} - docker tag hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG} hyperledger/fabric-${image} - docker tag hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG} hyperledger/fabric-${image}:${FABRIC_VERSION} - docker rmi -f hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG} +for image in baseos peer orderer ca tools orderer ccenv javaenv nodeenv tools; do + docker pull -q "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" + docker tag "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" hyperledger/fabric-${image} + docker tag "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" "hyperledger/fabric-${image}:${FABRIC_VERSION}" + docker rmi -f "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" done -docker pull -q hyperledger/fabric-couchdb +docker pull -q couchdb:3.1 docker images | grep hyperledger diff --git a/ci/scripts/run-test-network-basic.sh b/ci/scripts/run-test-network-basic.sh new file mode 100755 index 00000000..9d0fe2b9 --- /dev/null +++ b/ci/scripts/run-test-network-basic.sh @@ -0,0 +1,29 @@ +set -euo pipefail + +FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} +CHAINCODE_NAME=${CHAINCODE_NAME:-basic} + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + 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}" + + +# Run Javascript application +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 diff --git a/ci/scripts/run-test-network-ledger.sh b/ci/scripts/run-test-network-ledger.sh new file mode 100755 index 00000000..60b00612 --- /dev/null +++ b/ci/scripts/run-test-network-ledger.sh @@ -0,0 +1,21 @@ +set -euo pipefail + +FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} +CHAINCODE_NAME=${CHAINCODE_NAME:-ledger} + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + 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}" + +print "Stopping network" +./network.sh down diff --git a/ci/scripts/run-test-network-private.sh b/ci/scripts/run-test-network-private.sh new file mode 100755 index 00000000..c2578c40 --- /dev/null +++ b/ci/scripts/run-test-network-private.sh @@ -0,0 +1,21 @@ +set -euo pipefail + +FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} +CHAINCODE_NAME=${CHAINCODE_NAME:-private} + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + 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}" + +print "Stopping network" +./network.sh down diff --git a/ci/scripts/run-test-network-secured.sh b/ci/scripts/run-test-network-secured.sh new file mode 100755 index 00000000..b5bdc029 --- /dev/null +++ b/ci/scripts/run-test-network-secured.sh @@ -0,0 +1,21 @@ +set -euo pipefail + +FABRIC_VERSION=${FABRIC_VERSION:-2.2} +CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} +CHAINCODE_NAME=${CHAINCODE_NAME:-secured} + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + 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}" + +print "Stopping network" +./network.sh down diff --git a/ci/templates/test-network/azure-pipelines.yml b/ci/templates/test-network/azure-pipelines.yml deleted file mode 100644 index e934455c..00000000 --- a/ci/templates/test-network/azure-pipelines.yml +++ /dev/null @@ -1,14 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# - -steps: - - script: | - ./network.sh up createChannel -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml - ./network.sh deployCC -ccn basic -ccv 1 -ccl javascript -cci initLedger - ./network.sh deployCC -ccn basic -ccv 2 -ccl golang -cci initLedger - ./network.sh deployCC -ccn basic -ccv 3 -ccl typescript -cci initLedger - ./network.sh deployCC -ccn secure -ccv 1 -ccl golang - ./network.sh down - workingDirectory: test-network - displayName: Start Test Network From 59613c7e918b6a10714c18abca136ac487033f55 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Fri, 24 Jul 2020 13:50:31 -0400 Subject: [PATCH 32/45] Add Linters Signed-off-by: Brett Logan --- ci/azure-pipelines.yml | 49 ++++++++++++++++++++++++++++++++++++++++++ ci/scripts/lint.sh | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100755 ci/scripts/lint.sh diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 926d6e6f..9875d0da 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -8,6 +8,8 @@ trigger: variables: FABRIC_VERSION: 2.2 + GO_BIN: $(Build.Repository.LocalPath)/bin + GO_VER: 1.14.6 NODE_VER: 12.x PATH: $(Build.Repository.LocalPath)/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin @@ -68,6 +70,53 @@ jobs: - template: templates/install-deps.yml - template: templates/fabcar/azure-pipelines-typescript.yml + - job: Lint + displayName: Lint + pool: + vmImage: ubuntu-18.04 + strategy: + matrix: + Basic-Application-Javascript: + DIRECTORY: asset-transfer-basic + LANGUAGE: javascript + TYPE: application + Basic-Chaincode-Go: + DIRECTORY: asset-transfer-basic + LANGUAGE: go + TYPE: chaincode + Basic-Chaincode-Javascript: + DIRECTORY: asset-transfer-basic + LANGUAGE: javascript + TYPE: chaincode + Basic-Chaincode-Typescript: + DIRECTORY: asset-transfer-basic + LANGUAGE: typescript + TYPE: chaincode + Ledger-Chaincode-Go: + DIRECTORY: asset-transfer-ledger-queries + LANGUAGE: go + TYPE: chaincode + Private-Chaincode-Go: + DIRECTORY: asset-transfer-private-data + LANGUAGE: go + TYPE: chaincode + Secured-Chaincode-Go: + DIRECTORY: asset-transfer-secured-agreement + LANGUAGE: go + TYPE: chaincode + steps: + - task: GoTool@0 + inputs: + goBin: $(GO_BIN) + version: $(GO_VER) + displayName: Install GoLang + - task: NodeTool@0 + inputs: + versionSpec: $(NODE_VER) + displayName: Install Node.js + - script: ./ci/scripts/lint.sh + displayName: Lint Code + - job: TestNetworkBasic displayName: Test Network pool: diff --git a/ci/scripts/lint.sh b/ci/scripts/lint.sh new file mode 100755 index 00000000..9e8fd4e3 --- /dev/null +++ b/ci/scripts/lint.sh @@ -0,0 +1,47 @@ +set -euo pipefail + +function print() { + GREEN='\033[0;32m' + NC='\033[0m' + echo + echo -e "${GREEN}${1}${NC}" +} + +if [[ "${LANGUAGE}" == "go" ]]; then + go get golang.org/x/tools/cmd/goimports + + cd "${DIRECTORY}/${TYPE}-${LANGUAGE}" + print "Running go vet" + go vet ./... + + print "Running gofmt" + output=$(gofmt -l -s $(go list -f '{{.Dir}}' ./...)) + if [[ "${output}" != "" ]]; then + print "The following files contain formatting errors, please run 'gofmt -l -w ' to fix these issues:" + echo "${output}" + fi + + print "Running goimports" + output=$(goimports -l $(go list -f '{{.Dir}}' ./...)) + if [[ "${output}" != "" ]]; then + print "The following files contain import errors, please run 'goimports -l -w ' to fix these issues:" + echo "${output}" + fi +elif [[ "${LANGUAGE}" == "javascript" ]]; then + npm install -g eslint + cd "${DIRECTORY}/${TYPE}-${LANGUAGE}" + print "Running ESLint" + if [[ "${TYPE}" == "chaincode" ]]; then + eslint *.js */**.js + else + eslint *.js + fi +elif [[ "${LANGUAGE}" == "typescript" ]]; then + npm install -g typescript tslint + cd "${DIRECTORY}/${TYPE}-${LANGUAGE}" + print "Running TSLint" + tslint --project . +else + echo "Language not supported" + exit 1 +fi From 1c500ba30e791ed302fd740da59338ae592d263a Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Mon, 27 Jul 2020 11:43:02 -0400 Subject: [PATCH 33/45] Use Busybox to Remove Files and Folders We delete folders and files locally on the host system today. If the user is not a super user the files can't be deleted as they are created by a root user in the containers. This change uses busybox to perform the deletions instead so a root user can remove them. Signed-off-by: Brett Logan --- test-network/network.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test-network/network.sh b/test-network/network.sh index 6a3eb33b..6fe55118 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -417,16 +417,14 @@ function networkDown() { #Cleanup images removeUnwantedImages # remove orderer block and other channel configuration transactions and certs - rm -rf system-genesis-block/*.block organizations/peerOrganizations organizations/ordererOrganizations + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf system-genesis-block/*.block organizations/peerOrganizations organizations/ordererOrganizations' ## remove fabric ca artifacts - rm -rf organizations/fabric-ca/org1/msp organizations/fabric-ca/org1/tls-cert.pem organizations/fabric-ca/org1/ca-cert.pem organizations/fabric-ca/org1/IssuerPublicKey organizations/fabric-ca/org1/IssuerRevocationPublicKey organizations/fabric-ca/org1/fabric-ca-server.db - rm -rf organizations/fabric-ca/org2/msp organizations/fabric-ca/org2/tls-cert.pem organizations/fabric-ca/org2/ca-cert.pem organizations/fabric-ca/org2/IssuerPublicKey organizations/fabric-ca/org2/IssuerRevocationPublicKey organizations/fabric-ca/org2/fabric-ca-server.db - rm -rf organizations/fabric-ca/ordererOrg/msp organizations/fabric-ca/ordererOrg/tls-cert.pem organizations/fabric-ca/ordererOrg/ca-cert.pem organizations/fabric-ca/ordererOrg/IssuerPublicKey organizations/fabric-ca/ordererOrg/IssuerRevocationPublicKey organizations/fabric-ca/ordererOrg/fabric-ca-server.db - rm -rf addOrg3/fabric-ca/org3/msp addOrg3/fabric-ca/org3/tls-cert.pem addOrg3/fabric-ca/org3/ca-cert.pem addOrg3/fabric-ca/org3/IssuerPublicKey addOrg3/fabric-ca/org3/IssuerRevocationPublicKey addOrg3/fabric-ca/org3/fabric-ca-server.db - - + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/org1/msp organizations/fabric-ca/org1/tls-cert.pem organizations/fabric-ca/org1/ca-cert.pem organizations/fabric-ca/org1/IssuerPublicKey organizations/fabric-ca/org1/IssuerRevocationPublicKey organizations/fabric-ca/org1/fabric-ca-server.db' + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/org2/msp organizations/fabric-ca/org2/tls-cert.pem organizations/fabric-ca/org2/ca-cert.pem organizations/fabric-ca/org2/IssuerPublicKey organizations/fabric-ca/org2/IssuerRevocationPublicKey organizations/fabric-ca/org2/fabric-ca-server.db' + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/ordererOrg/msp organizations/fabric-ca/ordererOrg/tls-cert.pem organizations/fabric-ca/ordererOrg/ca-cert.pem organizations/fabric-ca/ordererOrg/IssuerPublicKey organizations/fabric-ca/ordererOrg/IssuerRevocationPublicKey organizations/fabric-ca/ordererOrg/fabric-ca-server.db' + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf addOrg3/fabric-ca/org3/msp addOrg3/fabric-ca/org3/tls-cert.pem addOrg3/fabric-ca/org3/ca-cert.pem addOrg3/fabric-ca/org3/IssuerPublicKey addOrg3/fabric-ca/org3/IssuerRevocationPublicKey addOrg3/fabric-ca/org3/fabric-ca-server.db' # remove channel and script artifacts - rm -rf channel-artifacts log.txt fabcar.tar.gz fabcar + docker run --rm -v $(pwd):/data busybox sh -c 'cd /data && rm -rf channel-artifacts log.txt fabcar.tar.gz fabcar' fi } From 6de6c77f8d6e7b3e6f71218cc2824461e4a81c9e Mon Sep 17 00:00:00 2001 From: Raghu Saxena Date: Thu, 30 Jul 2020 00:05:42 +0800 Subject: [PATCH 34/45] Update addOrg3.sh (#271) Fixed a comment which said `Create Org1 Identities` to `Create Org3 Identities` --- test-network/addOrg3/addOrg3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-network/addOrg3/addOrg3.sh b/test-network/addOrg3/addOrg3.sh index 62d4b946..47fb947f 100755 --- a/test-network/addOrg3/addOrg3.sh +++ b/test-network/addOrg3/addOrg3.sh @@ -67,7 +67,7 @@ function generateOrg3() { echo echo "##########################################################" - echo "############ Create Org1 Identities ######################" + echo "############ Create Org3 Identities ######################" echo "##########################################################" set -x From e51dfb9bce278c2bd6c211942c35574f205b669e Mon Sep 17 00:00:00 2001 From: r2roC Date: Thu, 23 Jul 2020 14:07:51 -0400 Subject: [PATCH 35/45] asset-transfer-basic Java CC. Adapted from fabcar. Unit tests are attached. Integration testing conducted using test network and asset-transfer-basic application-js. Signed-off-by: r2roC --- .gitignore | 2 + .../chaincode-java/.gitattributes | 6 + .../chaincode-java/build.gradle | 75 +++++ .../config/checkstyle/checkstyle.xml | 171 ++++++++++ .../config/checkstyle/suppressions.xml | 9 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55616 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + asset-transfer-basic/chaincode-java/gradlew | 188 +++++++++++ .../chaincode-java/gradlew.bat | 100 ++++++ .../chaincode-java/settings.gradle | 5 + .../fabric/samples/assettransfer/Asset.java | 93 ++++++ .../samples/assettransfer/AssetTransfer.java | 236 ++++++++++++++ .../samples/assettransfer/AssetTest.java | 74 +++++ .../assettransfer/AssetTransferTest.java | 305 ++++++++++++++++++ ci/azure-pipelines.yml | 7 + ci/scripts/lint.sh | 4 + 16 files changed, 1280 insertions(+) create mode 100644 asset-transfer-basic/chaincode-java/.gitattributes create mode 100644 asset-transfer-basic/chaincode-java/build.gradle create mode 100644 asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml create mode 100644 asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml create mode 100644 asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar create mode 100644 asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.properties create mode 100755 asset-transfer-basic/chaincode-java/gradlew create mode 100644 asset-transfer-basic/chaincode-java/gradlew.bat create mode 100644 asset-transfer-basic/chaincode-java/settings.gradle create mode 100644 asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java create mode 100644 asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java create mode 100644 asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java create mode 100644 asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java diff --git a/.gitignore b/.gitignore index 6ab3f7a5..7f4b61fc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ vendor/ .idea # Dependency directories node_modules/ +# Ignore Gradle build output directory +build diff --git a/asset-transfer-basic/chaincode-java/.gitattributes b/asset-transfer-basic/chaincode-java/.gitattributes new file mode 100644 index 00000000..00a51aff --- /dev/null +++ b/asset-transfer-basic/chaincode-java/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/asset-transfer-basic/chaincode-java/build.gradle b/asset-transfer-basic/chaincode-java/build.gradle new file mode 100644 index 00000000..5f90c5ac --- /dev/null +++ b/asset-transfer-basic/chaincode-java/build.gradle @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +plugins { + id 'application' + id 'checkstyle' + id 'jacoco' +} + +group 'org.hyperledger.fabric.samples' +version '1.0-SNAPSHOT' + +dependencies { + + compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' + implementation 'com.owlike:genson:1.5' + testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' + testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2' + testImplementation 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.mockito:mockito-core:2.+' +} + +repositories { + maven { + url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" + } + jcenter() + maven { + url 'https://jitpack.io' + } +} + +application { + mainClass = 'org.hyperledger.fabric.contract.ContractRouter' +} + +checkstyle { + toolVersion '8.21' + configFile file("config/checkstyle/checkstyle.xml") +} + +checkstyleMain { + source ='src/main/java' +} + +checkstyleTest { + source ='src/test/java' +} + +jacocoTestReport { + dependsOn test +} + +jacocoTestCoverageVerification { + violationRules { + rule { + limit { + minimum = 0.9 + } + } + } + + finalizedBy jacocoTestReport +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} + +check.dependsOn jacocoTestCoverageVerification +installDist.dependsOn check diff --git a/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml b/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..acd5df44 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml b/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml new file mode 100644 index 00000000..8c44b0a0 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a GIT binary patch literal 55616 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/asset-transfer-basic/chaincode-java/gradlew.bat b/asset-transfer-basic/chaincode-java/gradlew.bat new file mode 100644 index 00000000..9618d8d9 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/asset-transfer-basic/chaincode-java/settings.gradle b/asset-transfer-basic/chaincode-java/settings.gradle new file mode 100644 index 00000000..2633c4b9 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/settings.gradle @@ -0,0 +1,5 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +rootProject.name = 'basic' diff --git a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java new file mode 100644 index 00000000..803f22fb --- /dev/null +++ b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.fabric.samples.assettransfer; + +import java.util.Objects; + +import org.hyperledger.fabric.contract.annotation.DataType; +import org.hyperledger.fabric.contract.annotation.Property; + +import com.owlike.genson.annotation.JsonProperty; + +@DataType() +public final class Asset { + + @Property() + private final String assetID; + + @Property() + private final String color; + + @Property() + private final int size; + + @Property() + private final String owner; + + @Property() + private final int appraisedValue; + + public String getAssetID() { + return assetID; + } + + public String getColor() { + return color; + } + + public int getSize() { + return size; + } + + public String getOwner() { + return owner; + } + + public int getAppraisedValue() { + return appraisedValue; + } + + public Asset(@JsonProperty("assetID") final String assetID, @JsonProperty("color") final String color, + @JsonProperty("size") final int size, @JsonProperty("owner") final String owner, + @JsonProperty("appraisedValue") final int appraisedValue) { + this.assetID = assetID; + this.color = color; + this.size = size; + this.owner = owner; + this.appraisedValue = appraisedValue; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + + Asset other = (Asset) obj; + + return Objects.deepEquals( + new String[] {getAssetID(), getColor(), getOwner()}, + new String[] {other.getAssetID(), other.getColor(), other.getOwner()}) + && + Objects.deepEquals( + new int[] {getSize(), getAppraisedValue()}, + new int[] {other.getSize(), other.getAppraisedValue()}); + } + + @Override + public int hashCode() { + return Objects.hash(getAssetID(), getColor(), getSize(), getOwner(), getAppraisedValue()); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " [assetID=" + assetID + ", color=" + + color + ", size=" + size + ", owner=" + owner + ", appraisedValue=" + appraisedValue + "]"; + } +} diff --git a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java new file mode 100644 index 00000000..465d4694 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java @@ -0,0 +1,236 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.fabric.samples.assettransfer; + +import java.util.ArrayList; +import java.util.List; + +import org.hyperledger.fabric.contract.Context; +import org.hyperledger.fabric.contract.ContractInterface; +import org.hyperledger.fabric.contract.annotation.Contact; +import org.hyperledger.fabric.contract.annotation.Contract; +import org.hyperledger.fabric.contract.annotation.Default; +import org.hyperledger.fabric.contract.annotation.Info; +import org.hyperledger.fabric.contract.annotation.License; +import org.hyperledger.fabric.contract.annotation.Transaction; +import org.hyperledger.fabric.shim.ChaincodeException; +import org.hyperledger.fabric.shim.ChaincodeStub; +import org.hyperledger.fabric.shim.ledger.KeyValue; +import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; + +import com.owlike.genson.Genson; + +@Contract( + name = "basic", + info = @Info( + title = "Asset Transfer", + description = "The hyperlegendary asset transfer", + version = "0.0.1-SNAPSHOT", + license = @License( + name = "Apache 2.0 License", + url = "http://www.apache.org/licenses/LICENSE-2.0.html"), + contact = @Contact( + email = "a.transfer@example.com", + name = "Adrian Transfer", + url = "https://hyperledger.example.com"))) +@Default +public final class AssetTransfer implements ContractInterface { + + private final Genson genson = new Genson(); + + private enum AssetTransferErrors { + ASSET_NOT_FOUND, + ASSET_ALREADY_EXISTS + } + + /** + * Creates some initial assets on the ledger. + * + * @param ctx the transaction context + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public void InitLedger(final Context ctx) { + ChaincodeStub stub = ctx.getStub(); + + CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300); + CreateAsset(ctx, "asset2", "red", 5, "Brad", 400); + CreateAsset(ctx, "asset3", "green", 10, "Jin Soo", 500); + CreateAsset(ctx, "asset4", "yellow", 10, "Max", 600); + CreateAsset(ctx, "asset5", "black", 15, "Adrian", 700); + CreateAsset(ctx, "asset6", "white", 15, "Michel", 700); + + } + + /** + * Creates a new asset on the ledger. + * + * @param ctx the transaction context + * @param assetID the ID of the new asset + * @param color the color of the new asset + * @param size the size for the new asset + * @param owner the owner of the new asset + * @param appraisedValue the appraisedValue of the new asset + * @return the created asset + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size, + final String owner, final int appraisedValue) { + ChaincodeStub stub = ctx.getStub(); + + if (AssetExists(ctx, assetID)) { + String errorMessage = String.format("Asset %s already exists", assetID); + System.out.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString()); + } + + Asset asset = new Asset(assetID, color, size, owner, appraisedValue); + String assetJSON = genson.serialize(asset); + stub.putStringState(assetID, assetJSON); + + return asset; + } + + /** + * Retrieves an asset with the specified ID from the ledger. + * + * @param ctx the transaction context + * @param assetID the ID of the asset + * @return the asset found on the ledger if there was one + */ + @Transaction(intent = Transaction.TYPE.EVALUATE) + public Asset ReadAsset(final Context ctx, final String assetID) { + ChaincodeStub stub = ctx.getStub(); + String assetJSON = stub.getStringState(assetID); + + if (assetJSON == null || assetJSON.isEmpty()) { + String errorMessage = String.format("Asset %s does not exist", assetID); + System.out.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); + } + + Asset asset = genson.deserialize(assetJSON, Asset.class); + return asset; + } + + /** + * Updates the properties of an asset on the ledger. + * + * @param ctx the transaction context + * @param assetID the ID of the asset being updated + * @param color the color of the asset being updated + * @param size the size of the asset being updated + * @param owner the owner of the asset being updated + * @param appraisedValue the appraisedValue of the asset being updated + * @return the transferred asset + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size, + final String owner, final int appraisedValue) { + ChaincodeStub stub = ctx.getStub(); + + if (!AssetExists(ctx, assetID)) { + String errorMessage = String.format("Asset %s does not exist", assetID); + System.out.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); + } + + Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); + String newAssetJSON = genson.serialize(newAsset); + stub.putStringState(assetID, newAssetJSON); + + return newAsset; + } + + /** + * Deletes asset on the ledger. + * + * @param ctx the transaction context + * @param assetID the ID of the asset being deleted + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public void DeleteAsset(final Context ctx, final String assetID) { + ChaincodeStub stub = ctx.getStub(); + + if (!AssetExists(ctx, assetID)) { + String errorMessage = String.format("Asset %s does not exist", assetID); + System.out.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); + } + + stub.delState(assetID); + } + + /** + * Checks the existence of the asset on the ledger + * + * @param ctx the transaction context + * @param assetID the ID of the asset + * @return boolean indicating the existence of the asset + */ + @Transaction(intent = Transaction.TYPE.EVALUATE) + public boolean AssetExists(final Context ctx, final String assetID) { + ChaincodeStub stub = ctx.getStub(); + String assetJSON = stub.getStringState(assetID); + + return (assetJSON != null && !assetJSON.isEmpty()); + } + + /** + * Changes the owner of a asset on the ledger. + * + * @param ctx the transaction context + * @param assetID the ID of the asset being transferred + * @param newOwner the new owner + * @return the updated asset + */ + @Transaction(intent = Transaction.TYPE.SUBMIT) + public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) { + ChaincodeStub stub = ctx.getStub(); + String assetJSON = stub.getStringState(assetID); + + if (assetJSON == null || assetJSON.isEmpty()) { + String errorMessage = String.format("Asset %s does not exist", assetID); + System.out.println(errorMessage); + throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); + } + + Asset asset = genson.deserialize(assetJSON, Asset.class); + + Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); + String newAssetJSON = genson.serialize(newAsset); + stub.putStringState(assetID, newAssetJSON); + + return newAsset; + } + + /** + * Retrieves all assets from the ledger. + * + * @param ctx the transaction context + * @return array of assets found on the ledger + */ + @Transaction(intent = Transaction.TYPE.EVALUATE) + public String GetAllAssets(final Context ctx) { + ChaincodeStub stub = ctx.getStub(); + + List queryResults = new ArrayList(); + + // To retrieve all assets from the ledger use getStateByRange with empty startKey & endKey. + // Giving empty startKey & endKey is interpreted as all the keys from beginning to end. + // As another example, if you use startKey = 'asset0', endKey = 'asset9' , + // then getStateByRange will retrieve asset with keys between asset0 (inclusive) and asset9 (exclusive) in lexical order. + QueryResultsIterator results = stub.getStateByRange("", ""); + + for (KeyValue result: results) { + Asset asset = genson.deserialize(result.getStringValue(), Asset.class); + queryResults.add(asset); + System.out.println(asset.toString()); + } + + final String response = genson.serialize(queryResults); + + return response; + } +} diff --git a/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java new file mode 100644 index 00000000..7da94caa --- /dev/null +++ b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.fabric.samples.assettransfer; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public final class AssetTest { + + @Nested + class Equality { + + @Test + public void isReflexive() { + Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100); + + assertThat(asset).isEqualTo(asset); + } + + @Test + public void isSymmetric() { + Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100); + Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100); + + assertThat(assetA).isEqualTo(assetB); + assertThat(assetB).isEqualTo(assetA); + } + + @Test + public void isTransitive() { + Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100); + Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100); + Asset assetC = new Asset("asset1", "Blue", 20, "Guy", 100); + + assertThat(assetA).isEqualTo(assetB); + assertThat(assetB).isEqualTo(assetC); + assertThat(assetA).isEqualTo(assetC); + } + + @Test + public void handlesInequality() { + Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100); + Asset assetB = new Asset("asset2", "Red", 40, "Lady", 200); + + assertThat(assetA).isNotEqualTo(assetB); + } + + @Test + public void handlesOtherObjects() { + Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100); + String assetB = "not a asset"; + + assertThat(assetA).isNotEqualTo(assetB); + } + + @Test + public void handlesNull() { + Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100); + + assertThat(asset).isNotEqualTo(null); + } + } + + @Test + public void toStringIdentifiesAsset() { + Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100); + + assertThat(asset.toString()).isEqualTo("Asset@e04f6c53 [assetID=asset1, color=Blue, size=20, owner=Guy, appraisedValue=100]"); + } +} diff --git a/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java new file mode 100644 index 00000000..cbbb172f --- /dev/null +++ b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java @@ -0,0 +1,305 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.fabric.samples.assettransfer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.ThrowableAssert.catchThrowable; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.hyperledger.fabric.contract.Context; +import org.hyperledger.fabric.shim.ChaincodeException; +import org.hyperledger.fabric.shim.ChaincodeStub; +import org.hyperledger.fabric.shim.ledger.KeyValue; +import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; + +public final class AssetTransferTest { + + private final class MockKeyValue implements KeyValue { + + private final String key; + private final String value; + + MockKeyValue(final String key, final String value) { + super(); + this.key = key; + this.value = value; + } + + @Override + public String getKey() { + return this.key; + } + + @Override + public String getStringValue() { + return this.value; + } + + @Override + public byte[] getValue() { + return this.value.getBytes(); + } + + } + + private final class MockAssetResultsIterator implements QueryResultsIterator { + + private final List assetList; + + MockAssetResultsIterator() { + super(); + + assetList = new ArrayList(); + + assetList.add(new MockKeyValue("asset1", + "{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }")); + assetList.add(new MockKeyValue("asset2", + "{ \"assetID\": \"asset2\", \"color\": \"red\", \"size\": 5,\"owner\": \"Brad\", \"appraisedValue\": 400 }")); + assetList.add(new MockKeyValue("asset3", + "{ \"assetID\": \"asset3\", \"color\": \"green\", \"size\": 10,\"owner\": \"Jin Soo\", \"appraisedValue\": 500 }")); + assetList.add(new MockKeyValue("asset4", + "{ \"assetID\": \"asset4\", \"color\": \"yellow\", \"size\": 10,\"owner\": \"Max\", \"appraisedValue\": 600 }")); + assetList.add(new MockKeyValue("asset5", + "{ \"assetID\": \"asset5\", \"color\": \"black\", \"size\": 15,\"owner\": \"Adrian\", \"appraisedValue\": 700 }")); + assetList.add(new MockKeyValue("asset6", + "{ \"assetID\": \"asset6\", \"color\": \"white\", \"size\": 15,\"owner\": \"Michel\", \"appraisedValue\": 800 }")); + } + + @Override + public Iterator iterator() { + return assetList.iterator(); + } + + @Override + public void close() throws Exception { + // do nothing + } + + } + + @Test + public void invokeUnknownTransaction() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + + Throwable thrown = catchThrowable(() -> { + contract.unknownTransaction(ctx); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Undefined contract method called"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo(null); + + verifyZeroInteractions(ctx); + } + + @Nested + class InvokeReadAssetTransaction { + + @Test + public void whenAssetExists() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")) + .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"); + + Asset asset = contract.ReadAsset(ctx, "asset1"); + + assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Tomoko", 300)); + } + + @Test + public void whenAssetDoesNotExist() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")).thenReturn(""); + + Throwable thrown = catchThrowable(() -> { + contract.ReadAsset(ctx, "asset1"); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Asset asset1 does not exist"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes()); + } + } + + @Test + void invokeInitLedgerTransaction() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + + contract.InitLedger(ctx); + + InOrder inOrder = inOrder(stub); + inOrder.verify(stub).putStringState("asset1", "{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5}"); + inOrder.verify(stub).putStringState("asset2", "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5}"); + inOrder.verify(stub).putStringState("asset3", "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10}"); + inOrder.verify(stub).putStringState("asset4", "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10}"); + inOrder.verify(stub).putStringState("asset5", "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15}"); + + } + + @Nested + class InvokeCreateAssetTransaction { + + @Test + public void whenAssetExists() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")) + .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"); + + Throwable thrown = catchThrowable(() -> { + contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Asset asset1 already exists"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_ALREADY_EXISTS".getBytes()); + } + + @Test + public void whenAssetDoesNotExist() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")).thenReturn(""); + + Asset asset = contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60); + + assertThat(asset).isEqualTo(new Asset("asset1", "blue", 45, "Siobhán", 60)); + } + } + + @Test + void invokeGetAllAssetsTransaction() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStateByRange("", "")).thenReturn(new MockAssetResultsIterator()); + + String assets = contract.GetAllAssets(ctx); + + assertThat(assets).isEqualTo("[{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5}," + + "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5}," + + "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10}," + + "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10}," + + "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15}," + + "{\"appraisedValue\":800,\"assetID\":\"asset6\",\"color\":\"white\",\"owner\":\"Michel\",\"size\":15}]"); + + } + + @Nested + class TransferAssetTransaction { + + @Test + public void whenAssetExists() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")) + .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"); + + Asset asset = contract.TransferAsset(ctx, "asset1", "Dr Evil"); + + assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Dr Evil", 300)); + } + + @Test + public void whenAssetDoesNotExist() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")).thenReturn(""); + + Throwable thrown = catchThrowable(() -> { + contract.TransferAsset(ctx, "asset1", "Dr Evil"); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Asset asset1 does not exist"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes()); + } + } + + @Nested + class UpdateAssetTransaction { + + @Test + public void whenAssetExists() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")) + .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 45, \"owner\": \"Arturo\", \"appraisedValue\": 60 }"); + + Asset asset = contract.UpdateAsset(ctx, "asset1", "pink", 45, "Arturo", 600); + + assertThat(asset).isEqualTo(new Asset("asset1", "pink", 45, "Arturo", 600)); + } + + @Test + public void whenAssetDoesNotExist() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")).thenReturn(""); + + Throwable thrown = catchThrowable(() -> { + contract.TransferAsset(ctx, "asset1", "Alex"); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Asset asset1 does not exist"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes()); + } + } + + @Nested + class DeleteAssetTransaction { + + @Test + public void whenAssetDoesNotExist() { + AssetTransfer contract = new AssetTransfer(); + Context ctx = mock(Context.class); + ChaincodeStub stub = mock(ChaincodeStub.class); + when(ctx.getStub()).thenReturn(stub); + when(stub.getStringState("asset1")).thenReturn(""); + + Throwable thrown = catchThrowable(() -> { + contract.DeleteAsset(ctx, "asset1"); + }); + + assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() + .hasMessage("Asset asset1 does not exist"); + assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes()); + } + } +} diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 9875d0da..759d36a3 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -84,6 +84,10 @@ jobs: DIRECTORY: asset-transfer-basic LANGUAGE: go TYPE: chaincode + Basic-Chaincode-Java: + DIRECTORY: asset-transfer-basic + LANGUAGE: java + TYPE: chaincode Basic-Chaincode-Javascript: DIRECTORY: asset-transfer-basic LANGUAGE: javascript @@ -126,6 +130,9 @@ jobs: Basic-Go: CHAINCODE_NAME: basic CHAINCODE_LANGUAGE: go + Basic-Java: + CHAINCODE_NAME: basic + CHAINCODE_LANGUAGE: java Basic-Javascript: CHAINCODE_NAME: basic CHAINCODE_LANGUAGE: javascript diff --git a/ci/scripts/lint.sh b/ci/scripts/lint.sh index 9e8fd4e3..275b4d62 100755 --- a/ci/scripts/lint.sh +++ b/ci/scripts/lint.sh @@ -27,6 +27,10 @@ if [[ "${LANGUAGE}" == "go" ]]; then print "The following files contain import errors, please run 'goimports -l -w ' to fix these issues:" echo "${output}" fi +elif [[ "${LANGUAGE}" == "java" ]]; then + cd "${DIRECTORY}/${TYPE}-${LANGUAGE}" + print "Running Gradle Build" + ./gradlew build elif [[ "${LANGUAGE}" == "javascript" ]]; then npm install -g eslint cd "${DIRECTORY}/${TYPE}-${LANGUAGE}" From 1270c8418beff15633448d15d746a4444e6ce272 Mon Sep 17 00:00:00 2001 From: Chris Gabriel <33184046+denali49@users.noreply.github.com> Date: Mon, 3 Aug 2020 15:27:16 -0500 Subject: [PATCH 36/45] Fix typo in code comment (#275) Signed-off-by: Chris Gabriel --- asset-transfer-basic/application-javascript/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js index 5961455b..e044991f 100644 --- a/asset-transfer-basic/application-javascript/app.js +++ b/asset-transfer-basic/application-javascript/app.js @@ -80,7 +80,7 @@ async function main() { console.log('\n***********************'); console.log('Submit Transaction: CreateAsset asset13'); - //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraizedValue of 1300 + //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300 await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); console.log('Evaluate Transaction: ReadAsset asset13'); From 86d5a50a7c53f327784068188fabcae8bfa21eeb Mon Sep 17 00:00:00 2001 From: r2roC Date: Tue, 4 Aug 2020 17:05:45 -0400 Subject: [PATCH 37/45] asset-transfer-basic java application. (#276) Matches format of js app. Running TestApp: gradle runApp Signed-off-by: r2roC --- .../application-java/.gitattributes | 6 + .../application-java/build.gradle | 43 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + asset-transfer-basic/application-java/gradlew | 185 ++++++++++++++++++ .../application-java/gradlew.bat | 104 ++++++++++ .../application-java/settings.gradle | 10 + .../src/main/java/application/java/App.java | 116 +++++++++++ .../java/application/java/EnrollAdmin.java | 53 +++++ .../java/application/java/RegisterUser.java | 107 ++++++++++ .../src/main/resources/log4j.properties | 19 ++ 11 files changed, 648 insertions(+) create mode 100644 asset-transfer-basic/application-java/.gitattributes create mode 100644 asset-transfer-basic/application-java/build.gradle create mode 100644 asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar create mode 100644 asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties create mode 100755 asset-transfer-basic/application-java/gradlew create mode 100644 asset-transfer-basic/application-java/gradlew.bat create mode 100644 asset-transfer-basic/application-java/settings.gradle create mode 100644 asset-transfer-basic/application-java/src/main/java/application/java/App.java create mode 100644 asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java create mode 100644 asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java create mode 100644 asset-transfer-basic/application-java/src/main/resources/log4j.properties diff --git a/asset-transfer-basic/application-java/.gitattributes b/asset-transfer-basic/application-java/.gitattributes new file mode 100644 index 00000000..00a51aff --- /dev/null +++ b/asset-transfer-basic/application-java/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/asset-transfer-basic/application-java/build.gradle b/asset-transfer-basic/application-java/build.gradle new file mode 100644 index 00000000..e2eed626 --- /dev/null +++ b/asset-transfer-basic/application-java/build.gradle @@ -0,0 +1,43 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html + */ + +plugins { + // Apply the java plugin to add support for Java + id 'java' + + // Apply the application plugin to add support for building a CLI application. + id 'application' +} +ext { + javaMainClass = "application.java.App" +} + +repositories { + // Use jcenter for resolving dependencies. + // You can declare any Maven/Ivy/file repository here. + jcenter() +} + +dependencies { + // This dependency is used by the application. + implementation 'com.google.guava:guava:29.0-jre' + implementation 'org.hyperledger.fabric:fabric-gateway-java:2.1.1' +} + +application { + // Define the main class for the application. + mainClassName = 'application.java.App' +} + +// task for running the app after building dependencies +task runApp(type: Exec) { + dependsOn build + group = "Execution" + description = "Run the main class with ExecTask" + commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass +} \ No newline at end of file diff --git a/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 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 literal 0 HcmV?d00001 diff --git a/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/asset-transfer-basic/application-java/gradlew b/asset-transfer-basic/application-java/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/asset-transfer-basic/application-java/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/asset-transfer-basic/application-java/gradlew.bat b/asset-transfer-basic/application-java/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/asset-transfer-basic/application-java/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/asset-transfer-basic/application-java/settings.gradle b/asset-transfer-basic/application-java/settings.gradle new file mode 100644 index 00000000..5423bc7d --- /dev/null +++ b/asset-transfer-basic/application-java/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html + */ + +rootProject.name = 'application-java' diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/App.java b/asset-transfer-basic/application-java/src/main/java/application/java/App.java new file mode 100644 index 00000000..a0d1d5c7 --- /dev/null +++ b/asset-transfer-basic/application-java/src/main/java/application/java/App.java @@ -0,0 +1,116 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Running TestApp: +// gradle runApp + +package application.java; + +import java.nio.file.Path; +import java.nio.file.Paths; +import org.hyperledger.fabric.gateway.Contract; +import org.hyperledger.fabric.gateway.Gateway; +import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; + + +public class App { + + static { + System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true"); + } + + // helper function for getting connected to the gateway + public static Gateway connect() throws Exception{ + // Load a file system based wallet for managing identities. + Path walletPath = Paths.get("wallet"); + Wallet wallet = Wallets.newFileSystemWallet(walletPath); + // load a CCP + Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml"); + + Gateway.Builder builder = Gateway.createBuilder(); + builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true); + return builder.connect(); + } + + public static void main(String[] args) throws Exception { + // enrolls the admin and registers the user + try { + EnrollAdmin.main(null); + RegisterUser.main(null); + } catch (Exception e) { + System.err.println(e); + } + + // connect to the network and invoke the smart contract + try (Gateway gateway = connect()) { + + // get the network and contract + Network network = gateway.getNetwork("mychannel"); + Contract contract = network.getContract("basic"); + + byte[] result; + + System.out.println("Submit Transaction: InitLedger creates the initial set of assets on the ledger."); + contract.submitTransaction("InitLedger"); + + System.out.println("\n"); + result = contract.evaluateTransaction("GetAllAssets"); + System.out.println("Evaluate Transaction: GetAllAssets, result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: CreateAsset asset13"); + //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300 + contract.submitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300"); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: ReadAsset asset13"); + // ReadAsset returns an asset with given assetID + result = contract.evaluateTransaction("ReadAsset", "asset13"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: AssetExists asset1"); + // AssetExists returns "true" if an asset with given assetID exist + result = contract.evaluateTransaction("AssetExists", "asset1"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: UpdateAsset asset1, new AppraisedValue : 350"); + // UpdateAsset updates an existing asset with new properties. Same args as CreateAsset + contract.submitTransaction("UpdateAsset", "asset1", "blue", "5", "Tomoko", "350"); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: ReadAsset asset1"); + result = contract.evaluateTransaction("ReadAsset", "asset1"); + System.out.println("result: " + new String(result)); + + try { + System.out.println("\n"); + System.out.println("Submit Transaction: UpdateAsset asset70"); + //Non existing asset asset70 should throw Error + contract.submitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300"); + } catch (Exception e) { + System.err.println("Expected an error on UpdateAsset of non-existing Asset: " + e); + } + + System.out.println("\n"); + System.out.println("Submit Transaction: TransferAsset asset1 from owner Tomoko > owner Tom"); + // TransferAsset transfers an asset with given ID to new owner Tom + contract.submitTransaction("TransferAsset", "asset1", "Tom"); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: ReadAsset asset1"); + result = contract.evaluateTransaction("ReadAsset", "asset1"); + System.out.println("result: " + new String(result)); + } + catch(Exception e){ + System.err.println(e); + } + + } +} diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java b/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java new file mode 100644 index 00000000..563a35f1 --- /dev/null +++ b/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java @@ -0,0 +1,53 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package application.java; + +import java.nio.file.Paths; +import java.util.Properties; + +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; +import org.hyperledger.fabric.gateway.Identities; +import org.hyperledger.fabric.gateway.Identity; +import org.hyperledger.fabric.sdk.Enrollment; +import org.hyperledger.fabric.sdk.security.CryptoSuite; +import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory; +import org.hyperledger.fabric_ca.sdk.EnrollmentRequest; +import org.hyperledger.fabric_ca.sdk.HFCAClient; + +public class EnrollAdmin { + + public static void main(String[] args) throws Exception { + + // Create a CA client for interacting with the CA. + Properties props = new Properties(); + props.put("pemFile", + "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"); + props.put("allowAllHostNames", "true"); + HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props); + CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite(); + caClient.setCryptoSuite(cryptoSuite); + + // Create a wallet for managing identities + Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); + + // Check to see if we've already enrolled the admin user. + if (wallet.get("admin") != null) { + System.out.println("An identity for the admin user \"admin\" already exists in the wallet"); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest(); + enrollmentRequestTLS.addHost("localhost"); + enrollmentRequestTLS.setProfile("tls"); + Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS); + Identity user = Identities.newX509Identity("Org1MSP", enrollment); + wallet.put("admin", user); + System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet"); + } +} diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java b/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java new file mode 100644 index 00000000..367b4a39 --- /dev/null +++ b/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java @@ -0,0 +1,107 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package application.java; + +import java.nio.file.Paths; +import java.security.PrivateKey; +import java.util.Properties; +import java.util.Set; + +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; +import org.hyperledger.fabric.gateway.Identities; +import org.hyperledger.fabric.gateway.Identity; +import org.hyperledger.fabric.gateway.X509Identity; +import org.hyperledger.fabric.sdk.Enrollment; +import org.hyperledger.fabric.sdk.User; +import org.hyperledger.fabric.sdk.security.CryptoSuite; +import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory; +import org.hyperledger.fabric_ca.sdk.HFCAClient; +import org.hyperledger.fabric_ca.sdk.RegistrationRequest; + +public class RegisterUser { + + public static void main(String[] args) throws Exception { + + // Create a CA client for interacting with the CA. + Properties props = new Properties(); + props.put("pemFile", + "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"); + props.put("allowAllHostNames", "true"); + HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props); + CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite(); + caClient.setCryptoSuite(cryptoSuite); + + // Create a wallet for managing identities + Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); + + // Check to see if we've already enrolled the user. + if (wallet.get("appUser") != null) { + System.out.println("An identity for the user \"appUser\" already exists in the wallet"); + return; + } + + X509Identity adminIdentity = (X509Identity)wallet.get("admin"); + if (adminIdentity == null) { + System.out.println("\"admin\" needs to be enrolled and added to the wallet first"); + return; + } + User admin = new User() { + + @Override + public String getName() { + return "admin"; + } + + @Override + public Set getRoles() { + return null; + } + + @Override + public String getAccount() { + return null; + } + + @Override + public String getAffiliation() { + return "org1.department1"; + } + + @Override + public Enrollment getEnrollment() { + return new Enrollment() { + + @Override + public PrivateKey getKey() { + return adminIdentity.getPrivateKey(); + } + + @Override + public String getCert() { + return Identities.toPemString(adminIdentity.getCertificate()); + } + }; + } + + @Override + public String getMspId() { + return "Org1MSP"; + } + + }; + + // Register the user, enroll the user, and import the new identity into the wallet. + RegistrationRequest registrationRequest = new RegistrationRequest("appUser"); + registrationRequest.setAffiliation("org1.department1"); + registrationRequest.setEnrollmentID("appUser"); + String enrollmentSecret = caClient.register(registrationRequest, admin); + Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret); + Identity user = Identities.newX509Identity("Org1MSP", enrollment); + wallet.put("appUser", user); + System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet"); + } + +} diff --git a/asset-transfer-basic/application-java/src/main/resources/log4j.properties b/asset-transfer-basic/application-java/src/main/resources/log4j.properties new file mode 100644 index 00000000..f1f841fe --- /dev/null +++ b/asset-transfer-basic/application-java/src/main/resources/log4j.properties @@ -0,0 +1,19 @@ +# initialize root logger with level ERROR for stdout and fout +log4j.rootLogger=ERROR,stdout,fout +# set the log level for these components +log4j.logger.com.endeca=INFO +log4j.logger.com.endeca.itl.web.metrics=INFO + +# add a ConsoleAppender to the logger stdout to write to the console +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +# use a simple message format +log4j.appender.stdout.layout.ConversionPattern=%m%n + +# add a FileAppender to the logger fout +log4j.appender.fout=org.apache.log4j.FileAppender +# create a log file +log4j.appender.fout.File=crawl.log +log4j.appender.fout.layout=org.apache.log4j.PatternLayout +# use a more detailed message pattern +log4j.appender.fout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n From 6e49a18d90a2be5a3f4bc92bec45741057a01cdd Mon Sep 17 00:00:00 2001 From: r2roC Date: Tue, 4 Aug 2020 19:45:47 -0400 Subject: [PATCH 38/45] I removed some unnecessary comments that I left there by accident. (#278) I also capitalized 'Tom' as that is what the asset is initialized to. Signed-off-by: r2roC --- .../lib/asset_transfer_ledger_chaincode.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 a3d80bd7..44ba1038 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 @@ -5,24 +5,24 @@ // ====CHAINCODE EXECUTION SAMPLES (CLI) ================== // ==== Invoke assets ==== -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","35","tom","100"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","tom","150"]}' -// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","tom","200"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","35","Tom","100"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","Tom","150"]}' +// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","Tom","200"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAssetsBasedOnColor","blue","jerry"]}' // peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}' // ==== Query assets ==== // peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}' -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}' output issue go +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}' // peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}' // Rich Query (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsByOwner","tom"]}' output issue -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}' output issue go +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsByOwner","Tom"]}' output issue +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"Tom\"}}"]}' // Rich Query with Pagination (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}' //error: invalid bookmark +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"Tom\"}}","3",""]}' // INDEXES TO SUPPORT COUCHDB RICH QUERIES // @@ -60,10 +60,10 @@ // curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' // Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): -// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' +// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"Tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}' 'use strict'; From d2fb039ba5f5c072041218cc9ecf2c0c10d8ec4e Mon Sep 17 00:00:00 2001 From: aldredb <45155724+aldredb@users.noreply.github.com> Date: Thu, 6 Aug 2020 01:22:21 +0800 Subject: [PATCH 39/45] Correct swap JSON payload in interest_rate_swaps example (#279) Corrected swap JSON payload so that the JSON object can be unmarshalled properly Signed-off-by: Aldred Benedict --- interest_rate_swaps/README.md | 2 +- interest_rate_swaps/network/scripts/script.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interest_rate_swaps/README.md b/interest_rate_swaps/README.md index bcb16519..439570fd 100644 --- a/interest_rate_swaps/README.md +++ b/interest_rate_swaps/README.md @@ -151,7 +151,7 @@ specified as providing this reference rate in the init parameters. To create a swap named "myswap": ``` -peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":100000,\"FixedRate\":400,\"FloatingRate\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}' +peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":100000,\"FixedRateBPS\":400,\"FloatingRateBPS\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}' ``` Note that the transaction is endorsed by both parties that are part of this swap as well as the auditor. Since the principal amount in this case is lower diff --git a/interest_rate_swaps/network/scripts/script.sh b/interest_rate_swaps/network/scripts/script.sh index 4514c837..83cd90fd 100755 --- a/interest_rate_swaps/network/scripts/script.sh +++ b/interest_rate_swaps/network/scripts/script.sh @@ -115,7 +115,7 @@ createSwap() { CORE_PEER_ADDRESS=irs-partya:7051 CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/User1@partya.example.com/msp echo "===================== Invoking chaincode ===================== " - peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":100000,\"FixedRate\":400,\"FloatingRate\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}' + peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":100000,\"FixedRateBPS\":400,\"FloatingRateBPS\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}' echo "===================== Chaincode invoked ===================== " } From 21572f833fab0a9f08d1e8646367b9dfddd94ace Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 13:25:32 -0400 Subject: [PATCH 40/45] Bump elliptic from 6.5.2 to 6.5.3 in /fabcar/javascript (#273) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fabcar/javascript/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fabcar/javascript/package-lock.json b/fabcar/javascript/package-lock.json index 2446ee3e..065751b8 100644 --- a/fabcar/javascript/package-lock.json +++ b/fabcar/javascript/package-lock.json @@ -687,9 +687,9 @@ } }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", From e2817762f1b559f6faf4b6a56479b0e436e00e5f Mon Sep 17 00:00:00 2001 From: Dereck Date: Wed, 5 Aug 2020 20:12:12 -0400 Subject: [PATCH 41/45] Add chaincode-external for asset-transfer-basic sample. (#234) Signed-off-by: Chongxin Luo --- .../chaincode-external/.dockerignore | 5 + .../chaincode-external/.gitignore | 4 + .../chaincode-external/Dockerfile | 17 ++ .../chaincode-external/README.md | 83 +++++++ .../chaincode-external/assetTransfer.go | 219 ++++++++++++++++++ .../chaincode-external/chaincode.env.example | 8 + .../chaincode-external/go.mod | 8 + .../chaincode-external/go.sum | 146 ++++++++++++ .../chaincode-external/metadata.json | 4 + .../sampleBuilder/bin/build | 22 ++ .../sampleBuilder/bin/detect | 13 ++ .../sampleBuilder/bin/release | 23 ++ 12 files changed, 552 insertions(+) create mode 100644 asset-transfer-basic/chaincode-external/.dockerignore create mode 100644 asset-transfer-basic/chaincode-external/.gitignore create mode 100644 asset-transfer-basic/chaincode-external/Dockerfile create mode 100755 asset-transfer-basic/chaincode-external/README.md create mode 100644 asset-transfer-basic/chaincode-external/assetTransfer.go create mode 100644 asset-transfer-basic/chaincode-external/chaincode.env.example create mode 100644 asset-transfer-basic/chaincode-external/go.mod create mode 100644 asset-transfer-basic/chaincode-external/go.sum create mode 100644 asset-transfer-basic/chaincode-external/metadata.json create mode 100755 asset-transfer-basic/chaincode-external/sampleBuilder/bin/build create mode 100755 asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect create mode 100755 asset-transfer-basic/chaincode-external/sampleBuilder/bin/release diff --git a/asset-transfer-basic/chaincode-external/.dockerignore b/asset-transfer-basic/chaincode-external/.dockerignore new file mode 100644 index 00000000..61260e58 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/.dockerignore @@ -0,0 +1,5 @@ +chaincode.env* +*.json +*.md +*.tar.gz +*.tgz diff --git a/asset-transfer-basic/chaincode-external/.gitignore b/asset-transfer-basic/chaincode-external/.gitignore new file mode 100644 index 00000000..a31a0d12 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/.gitignore @@ -0,0 +1,4 @@ +chaincode.env +connection.json +*.tar.gz +*.tgz diff --git a/asset-transfer-basic/chaincode-external/Dockerfile b/asset-transfer-basic/chaincode-external/Dockerfile new file mode 100644 index 00000000..36378377 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/Dockerfile @@ -0,0 +1,17 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +ARG GO_VER=1.14.4 +ARG ALPINE_VER=3.12 + +FROM golang:${GO_VER}-alpine${ALPINE_VER} + +WORKDIR /go/src/github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external +COPY . . + +RUN go get -d -v ./... +RUN go install -v ./... + +EXPOSE 9999 +CMD ["external"] diff --git a/asset-transfer-basic/chaincode-external/README.md b/asset-transfer-basic/chaincode-external/README.md new file mode 100755 index 00000000..2522736e --- /dev/null +++ b/asset-transfer-basic/chaincode-external/README.md @@ -0,0 +1,83 @@ +# Asset-Transfer-Basic as an external service + +See the "Chaincode as an external service" documentation for running chaincode as an external service. +This includes details of the external builder and launcher scripts which peers in your Fabric network will require. + +The Asset-Transfer-Basic chaincode requires two environment variables to run, `CHAINCODE_SERVER_ADDRESS` and `CHAINCODE_ID`, which are described in the `chaincode.env.example` file. Copy this file to `chaincode.env` before continuing. + +**Note:** each organization in a Asset-Transfer-Basic network will need to follow the instructions below to host their own instance of the Asset-Transfer-Basic external service. + +## Packaging peer image + +External Builders and Launchers is an advanced feature that will likely require custom packaging of the peer image. The following steps is the basic walk through to build a peer binary with external builders and launcher capability, please refer to ["External Builders and Launchers"](https://hyperledger-fabric.readthedocs.io/en/release-2.2/cc_launcher.html) for more details. + +First of all, download the Hyperledger Fabric source code from ["Fabric github repository"](https://github.com/hyperledger/fabric/tree/master), a version greater than 2.0 is required. + +Open the `core.yaml` configuration file under `fabric/sampleconfig` + +Modify the field `externalBuilders` as the following: +``` +externalBuilders: + - path: /full path to fabric-samples directory/asset-transfer-basic/chaincode-external/sampleBuilder + name: external + environmentWhitelist: + - GOPROXY +``` +This configuration sets the name of the external builder as `external`, and the path of the builder scripts to the sampleBuilder scripts provided in this sample. + +With completing the configuration setup for peer. We are ready to build the peer binary using the command `make peer`. The peer binary will be located at `fabric/build/bin/peer`. + +## Packaging and installing Chaincode + +Make sure the value of `CHAINCODE_SERVER_ADDRESS` in `chaincode.env` is correct for the Asset-Transfer-Basic external service you will be running. + +The peer needs a `connection.json` configuration file so that it can connect to the external Asset-Transfer-Basic service. +Use the `CHAINCODE_SERVER_ADDRESS` value in `chaincode.env` to create the `connection.json` file with the following command (requires [jq](https://stedolan.github.io/jq/)): + +``` +env $(cat chaincode.env | grep -v "#" | xargs) jq -n '{"address":env.CHAINCODE_SERVER_ADDRESS,"dial_timeout": "10s","tls_required": false}' > connection.json +``` + +Add this file to a `code.tar.gz` archive ready for adding to a Asset-Transfer-Basic external service package: + +``` +tar cfz code.tar.gz connection.json +``` + +Package the Asset-Transfer-Basic external service using the supplied `metadata.json` file: + +``` +tar cfz asset-transfer-basic-pkg.tgz metadata.json code.tar.gz +``` + +Install the `asset-transfer-basic-pkg.tgz` chaincode as usual, for example: + +``` +peer lifecycle chaincode install ./asset-transfer-basic-pkg.tgz +``` + +Query installed chaincode to get chaincode package-id: + +``` +peer lifecycle chaincode queryinstalled --peerAddresses {PEER_ADDRESS} +``` + +Edit the `chaincode.env` file to configure the `CHAINCODE_ID` variable with obtained chaincode package-id. + +## Running the Asset-Transfer-Basic external service + +To run the service in a container, build a Asset-Transfer-Basic docker image: + +``` +docker build -t hyperledger/asset-transfer-basic . +``` + +Edit the `chaincode.env` file to configure the `CHAINCODE_ID` variable before starting a Asset-Transfer-Basic container using the following command: + +``` +docker run -it --rm --name asset-transfer-basic.org1.example.com --hostname asset-transfer-basic.org1.example.com --env-file chaincode.env --network=net_test hyperledger/asset-transfer-basic +``` + +## Starting the Asset-Transfer-Basic external service + +Complete the remaining lifecycle steps to start the Asset-Transfer-Basic chaincode! diff --git a/asset-transfer-basic/chaincode-external/assetTransfer.go b/asset-transfer-basic/chaincode-external/assetTransfer.go new file mode 100644 index 00000000..f0f21b83 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/assetTransfer.go @@ -0,0 +1,219 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + + "github.com/hyperledger/fabric-chaincode-go/shim" + "github.com/hyperledger/fabric-contract-api-go/contractapi" +) + +type serverConfig struct { + CCID string + Address string +} + +// SmartContract provides functions for managing a car +type SmartContract struct { + contractapi.Contract +} + +// Asset describes basic details of what makes up a simple asset +type Asset struct { + ID string `json:"ID"` + Color string `json:"color"` + Size int `json:"size"` + Owner string `json:"owner"` + AppraisedValue int `json:"appraisedValue"` +} + +// QueryResult structure used for handling result of query +type QueryResult struct { + Key string `json:"Key"` + Record *Asset +} + +// InitLedger adds a base set of cars to the ledger +func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { + assets := []Asset{ + {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, + {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, + {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, + {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, + {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, + {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, + } + + for _, asset := range assets { + assetJSON, err := json.Marshal(asset) + if err != nil { + return err + } + + err = ctx.GetStub().PutState(asset.ID, assetJSON) + if err != nil { + return fmt.Errorf("failed to put to world state: %v", err) + } + } + + return nil +} + +// CreateAsset issues a new asset to the world state with given details. +func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, color, owner string, size, appraisedValue int) error { + exists, err := s.AssetExists(ctx, id) + if err != nil { + return err + } + if exists { + return fmt.Errorf("the asset %s already exists", id) + } + asset := Asset{ + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + } + + assetJSON, err := json.Marshal(asset) + if err != nil { + return err + } + + return ctx.GetStub().PutState(id, assetJSON) +} + +// ReadAsset returns the asset stored in the world state with given id. +func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) { + assetJSON, err := ctx.GetStub().GetState(id) + if err != nil { + return nil, fmt.Errorf("failed to read from world state. %s", err.Error()) + } + if assetJSON == nil { + return nil, fmt.Errorf("the asset %s does not exist", id) + } + + var asset *Asset + err = json.Unmarshal(assetJSON, asset) + if err != nil { + return nil, err + } + + return asset, nil +} + +// UpdateAsset updates an existing asset in the world state with provided parameters. +func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id, color, owner string, size, appraisedValue int) error { + exists, err := s.AssetExists(ctx, id) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("the asset %s does not exist", id) + } + + // overwritting original asset with new asset + asset := Asset{ + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + } + + assetJSON, err := json.Marshal(asset) + if err != nil { + return err + } + + return ctx.GetStub().PutState(id, assetJSON) +} + +// DeleteAsset deletes an given asset from the world state. +func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error { + exists, err := s.AssetExists(ctx, id) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("the asset %s does not exist", id) + } + + return ctx.GetStub().DelState(id) +} + +// AssetExists returns true when asset with given ID exists in world state +func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { + assetJSON, err := ctx.GetStub().GetState(id) + if err != nil { + return false, fmt.Errorf("failed to read from world state. %s", err.Error()) + } + + return assetJSON != nil, nil +} + +// GetAllAssets returns all assets found in world state +func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) { + // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. + resultsIterator, err := ctx.GetStub().GetStateByRange("", "") + + if err != nil { + return nil, err + } + defer resultsIterator.Close() + + var results []QueryResult + + for resultsIterator.HasNext() { + queryResponse, err := resultsIterator.Next() + + if err != nil { + return nil, err + } + + var asset *Asset + err = json.Unmarshal(queryResponse.Value, asset) + if err != nil { + return nil, err + } + + queryResult := QueryResult{Key: queryResponse.Key, Record: asset} + results = append(results, queryResult) + } + + return results, nil +} + +func main() { + // See chaincode.env.example + config := serverConfig{ + CCID: os.Getenv("CHAINCODE_ID"), + Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"), + } + + chaincode, err := contractapi.NewChaincode(&SmartContract{}) + + if err != nil { + log.Panicf("error create asset-transfer-basic chaincode: %s", err) + } + + server := &shim.ChaincodeServer{ + CCID: config.CCID, + Address: config.Address, + CC: chaincode, + TLSProps: shim.TLSProperties{ + Disabled: true, + }, + } + + if err := server.Start(); err != nil { + log.Panicf("error starting asset-transfer-basic chaincode: %s", err) + } +} diff --git a/asset-transfer-basic/chaincode-external/chaincode.env.example b/asset-transfer-basic/chaincode-external/chaincode.env.example new file mode 100644 index 00000000..1b5bfe9d --- /dev/null +++ b/asset-transfer-basic/chaincode-external/chaincode.env.example @@ -0,0 +1,8 @@ +# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can +# connect to the chaincode server +CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999 + +# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode +# on install. The `peer lifecycle chaincode queryinstalled` command can be +# used to get the ID after install if required +CHAINCODE_ID=asset-transfer-basic:... diff --git a/asset-transfer-basic/chaincode-external/go.mod b/asset-transfer-basic/chaincode-external/go.mod new file mode 100644 index 00000000..d8e3eecf --- /dev/null +++ b/asset-transfer-basic/chaincode-external/go.mod @@ -0,0 +1,8 @@ +module github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external + +go 1.14 + +require ( + github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 + github.com/hyperledger/fabric-contract-api-go v1.1.0 +) diff --git a/asset-transfer-basic/chaincode-external/go.sum b/asset-transfer-basic/chaincode-external/go.sum new file mode 100644 index 00000000..a159a45f --- /dev/null +++ b/asset-transfer-basic/chaincode-external/go.sum @@ -0,0 +1,146 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= +github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc= +github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4= +github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E= +github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= +github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E= +github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= +golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/asset-transfer-basic/chaincode-external/metadata.json b/asset-transfer-basic/chaincode-external/metadata.json new file mode 100644 index 00000000..f230c177 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/metadata.json @@ -0,0 +1,4 @@ +{ + "type": "external", + "label": "asset-transfer-basic" +} diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/build b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/build new file mode 100755 index 00000000..03cb3b54 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/build @@ -0,0 +1,22 @@ +#!/bin/bash + +echo "!! DL build script invoked" +set -euo pipefail + +SOURCE=$1 +OUTPUT=$3 + +#external chaincodes expect connection.json file in the chaincode package +if [ ! -f "$SOURCE/connection.json" ]; then + >&2 echo "$SOURCE/connection.json not found" + exit 1 +fi + +#simply copy the endpoint information to specified output location +cp $SOURCE/connection.json $OUTPUT/connection.json + +if [ -d "$SOURCE/metadata" ]; then + cp -a $SOURCE/metadata $OUTPUT/metadata +fi + +exit 0 \ No newline at end of file diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect new file mode 100755 index 00000000..68ea84a6 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect @@ -0,0 +1,13 @@ +#!/bin/bash + +echo "!! DL detect script invoked" + +set -euo pipefail + +METADIR=$2 +#check if the "type" field is set to "external" +if [ "$(jq -r .type "$METADIR/metadata.json")" == "external" ]; then + exit 0 +fi + +exit 1 \ No newline at end of file diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release new file mode 100755 index 00000000..e37b1c13 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release @@ -0,0 +1,23 @@ +#!/bin/bash + +echo "!! DL release script invoked" +set -euo pipefail + +BLD="$1" +RELEASE="$2" + +if [ -d "$BLD/metadata" ]; then + cp -a "$BLD/metadata/"* "$RELEASE/" +fi + +#external chaincodes expect artifacts to be placed under "$RELEASE"/chaincode/server +if [ -f $BLD/connection.json ]; then + mkdir -p "$RELEASE"/chaincode/server + cp $BLD/connection.json "$RELEASE"/chaincode/server + + #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE"/chaincode/server/tls) + + exit 0 +fi + +exit 1 \ No newline at end of file From 42026e2fd4da691ca7168da1655fb9bec8162084 Mon Sep 17 00:00:00 2001 From: Arnaud J Le Hors Date: Wed, 5 Aug 2020 12:48:00 -0700 Subject: [PATCH 42/45] Improve unmarshalling in asset-transfer-basic Go cc Although the current code works thanks to Go being quite forgiving when it comes to pointers, it doesn't exhibit the best coding style. This patch addresses this. Signed-off-by: Arnaud J Le Hors --- .../chaincode-go/chaincode/smartcontract.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go index 8b48798c..71e8dd84 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go @@ -82,13 +82,13 @@ func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, i return nil, fmt.Errorf("the asset %s does not exist", id) } - var asset *Asset + var asset Asset err = json.Unmarshal(assetJSON, &asset) if err != nil { return nil, err } - return asset, nil + return &asset, nil } // UpdateAsset updates an existing asset in the world state with provided parameters. @@ -173,12 +173,12 @@ func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface return nil, err } - var asset *Asset + var asset Asset err = json.Unmarshal(queryResponse.Value, &asset) if err != nil { return nil, err } - assets = append(assets, asset) + assets = append(assets, &asset) } return assets, nil From cb8e03b7266b9c2b200dc0a2ee3690d56c6bf99b Mon Sep 17 00:00:00 2001 From: r2roC Date: Thu, 6 Aug 2020 12:00:26 -0400 Subject: [PATCH 43/45] asset-transfer-ledger-queries java application. (#280) Running TestApp: gradle runApp Signed-off-by: r2roC --- .../application-java/.gitattributes | 6 + .../application-java/build.gradle | 43 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../application-java/gradlew | 185 ++++++++++++++++++ .../application-java/gradlew.bat | 104 ++++++++++ .../application-java/settings.gradle | 10 + .../src/main/java/application/java/App.java | 142 ++++++++++++++ .../java/application/java/EnrollAdmin.java | 53 +++++ .../java/application/java/RegisterUser.java | 107 ++++++++++ .../src/main/resources/log4j.properties | 19 ++ 11 files changed, 674 insertions(+) create mode 100644 asset-transfer-ledger-queries/application-java/.gitattributes create mode 100644 asset-transfer-ledger-queries/application-java/build.gradle create mode 100644 asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar create mode 100644 asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties create mode 100755 asset-transfer-ledger-queries/application-java/gradlew create mode 100644 asset-transfer-ledger-queries/application-java/gradlew.bat create mode 100644 asset-transfer-ledger-queries/application-java/settings.gradle create mode 100644 asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java create mode 100644 asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java create mode 100644 asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java create mode 100644 asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties diff --git a/asset-transfer-ledger-queries/application-java/.gitattributes b/asset-transfer-ledger-queries/application-java/.gitattributes new file mode 100644 index 00000000..00a51aff --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/asset-transfer-ledger-queries/application-java/build.gradle b/asset-transfer-ledger-queries/application-java/build.gradle new file mode 100644 index 00000000..e2eed626 --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/build.gradle @@ -0,0 +1,43 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html + */ + +plugins { + // Apply the java plugin to add support for Java + id 'java' + + // Apply the application plugin to add support for building a CLI application. + id 'application' +} +ext { + javaMainClass = "application.java.App" +} + +repositories { + // Use jcenter for resolving dependencies. + // You can declare any Maven/Ivy/file repository here. + jcenter() +} + +dependencies { + // This dependency is used by the application. + implementation 'com.google.guava:guava:29.0-jre' + implementation 'org.hyperledger.fabric:fabric-gateway-java:2.1.1' +} + +application { + // Define the main class for the application. + mainClassName = 'application.java.App' +} + +// task for running the app after building dependencies +task runApp(type: Exec) { + dependsOn build + group = "Execution" + description = "Run the main class with ExecTask" + commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass +} \ No newline at end of file diff --git a/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 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 literal 0 HcmV?d00001 diff --git a/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/asset-transfer-ledger-queries/application-java/gradlew b/asset-transfer-ledger-queries/application-java/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/asset-transfer-ledger-queries/application-java/gradlew.bat b/asset-transfer-ledger-queries/application-java/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/asset-transfer-ledger-queries/application-java/settings.gradle b/asset-transfer-ledger-queries/application-java/settings.gradle new file mode 100644 index 00000000..5423bc7d --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html + */ + +rootProject.name = 'application-java' diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java new file mode 100644 index 00000000..2a1b74cb --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java @@ -0,0 +1,142 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Running TestApp: +// gradle runApp + +package application.java; + +import java.nio.file.Path; +import java.nio.file.Paths; +import org.hyperledger.fabric.gateway.Contract; +import org.hyperledger.fabric.gateway.Gateway; +import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; + + +public class App { + + static { + System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true"); + } + + // helper function for getting connected to the gateway + public static Gateway connect() throws Exception{ + // Load a file system based wallet for managing identities. + Path walletPath = Paths.get("wallet"); + Wallet wallet = Wallets.newFileSystemWallet(walletPath); + // load a CCP + Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml"); + + Gateway.Builder builder = Gateway.createBuilder(); + builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true); + return builder.connect(); + } + + public static void main(String[] args) throws Exception { + // enrolls the admin and registers the user + try { + EnrollAdmin.main(null); + RegisterUser.main(null); + } catch (Exception e) { + System.err.println(e); + } + + // connect to the network and invoke the smart contract + try (Gateway gateway = connect()) { + + // get the network and contract + Network network = gateway.getNetwork("mychannel"); + Contract contract = network.getContract("ledger"); + + byte[] result; + + System.out.println("Submit Transaction: InitLedger creates the initial set of assets on the ledger."); + contract.submitTransaction("InitLedger"); + + System.out.println("\n"); + // passing in 2 empty strings will query all the assets + result = contract.evaluateTransaction("GetAssetsByRange", "", ""); + System.out.println("Evaluate Transaction: GetAssetsByRange, result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: CreateAsset asset13"); + //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300 + contract.submitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300"); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: ReadAsset asset13"); + // ReadAsset returns an asset with given assetID + result = contract.evaluateTransaction("ReadAsset", "asset13"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: AssetExists asset1"); + // AssetExists returns "true" if an asset with given assetID exist + result = contract.evaluateTransaction("AssetExists", "asset1"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: DeleteAsset asset1"); + contract.submitTransaction("DeleteAsset", "asset1"); + + System.out.println("\n"); + System.out.println("Evaluate Transaction: AssetExists asset1"); + // AssetExists returns "true" if an asset with given assetID exist + result = contract.evaluateTransaction("AssetExists", "asset1"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: TransferAsset asset2 from owner Tomoko > owner Tom"); + // TransferAsset transfers an asset with given ID to new owner Tom + contract.submitTransaction("TransferAsset", "asset2", "Tom"); + + // Rich Query with Pagination (Only supported if CouchDB is used as state database) + System.out.println("\n"); + System.out.println("Evaluate Transaction:QueryAssetsWithPagination Tom's assets"); + result = contract.evaluateTransaction("QueryAssetsWithPagination","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3",""); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Submit Transaction: TransferAssetByColor yellow assets > newOwner Michel"); + contract.submitTransaction("TransferAssetByColor", "yellow", "Michel"); + + // Rich Query (Only supported if CouchDB is used as state database): + System.out.println("\n"); + System.out.println("Evaluate Transaction:QueryAssetsByOwner Michel"); + result = contract.evaluateTransaction("QueryAssetsByOwner", "Michel"); + System.out.println("result: " + new String(result)); + + System.out.println("\n"); + System.out.println("Evaluate Transaction:GetAssetHistory asset13"); + result = contract.evaluateTransaction("GetAssetHistory", "asset13"); + System.out.println("result: " + new String(result)); + + // Rich Query (Only supported if CouchDB is used as state database): + System.out.println("\n"); + System.out.println("Evaluate Transaction:QueryAssets assets of size 15"); + result = contract.evaluateTransaction("QueryAssets", "{\"selector\":{\"size\":15}}"); + System.out.println("result: " + new String(result)); + + // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): + System.out.println("\n"); + System.out.println("Evaluate Transaction:QueryAssets Jin Soo's assets"); + result = contract.evaluateTransaction("QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Jin Soo\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"); + System.out.println("result: " + new String(result)); + + // Rich Query with Pagination (Only supported if CouchDB is used as state database) + System.out.println("\n"); + System.out.println("Evaluate Transaction:GetAssetsByRangeWithPagination assets 3-5"); + result = contract.evaluateTransaction("GetAssetsByRangeWithPagination", "asset3", "asset6", "3",""); + System.out.println("result: " + new String(result)); + } + catch(Exception e){ + System.err.println(e); + } + + } +} diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java new file mode 100644 index 00000000..563a35f1 --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java @@ -0,0 +1,53 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package application.java; + +import java.nio.file.Paths; +import java.util.Properties; + +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; +import org.hyperledger.fabric.gateway.Identities; +import org.hyperledger.fabric.gateway.Identity; +import org.hyperledger.fabric.sdk.Enrollment; +import org.hyperledger.fabric.sdk.security.CryptoSuite; +import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory; +import org.hyperledger.fabric_ca.sdk.EnrollmentRequest; +import org.hyperledger.fabric_ca.sdk.HFCAClient; + +public class EnrollAdmin { + + public static void main(String[] args) throws Exception { + + // Create a CA client for interacting with the CA. + Properties props = new Properties(); + props.put("pemFile", + "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"); + props.put("allowAllHostNames", "true"); + HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props); + CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite(); + caClient.setCryptoSuite(cryptoSuite); + + // Create a wallet for managing identities + Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); + + // Check to see if we've already enrolled the admin user. + if (wallet.get("admin") != null) { + System.out.println("An identity for the admin user \"admin\" already exists in the wallet"); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest(); + enrollmentRequestTLS.addHost("localhost"); + enrollmentRequestTLS.setProfile("tls"); + Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS); + Identity user = Identities.newX509Identity("Org1MSP", enrollment); + wallet.put("admin", user); + System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet"); + } +} diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java new file mode 100644 index 00000000..367b4a39 --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java @@ -0,0 +1,107 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package application.java; + +import java.nio.file.Paths; +import java.security.PrivateKey; +import java.util.Properties; +import java.util.Set; + +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallets; +import org.hyperledger.fabric.gateway.Identities; +import org.hyperledger.fabric.gateway.Identity; +import org.hyperledger.fabric.gateway.X509Identity; +import org.hyperledger.fabric.sdk.Enrollment; +import org.hyperledger.fabric.sdk.User; +import org.hyperledger.fabric.sdk.security.CryptoSuite; +import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory; +import org.hyperledger.fabric_ca.sdk.HFCAClient; +import org.hyperledger.fabric_ca.sdk.RegistrationRequest; + +public class RegisterUser { + + public static void main(String[] args) throws Exception { + + // Create a CA client for interacting with the CA. + Properties props = new Properties(); + props.put("pemFile", + "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"); + props.put("allowAllHostNames", "true"); + HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props); + CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite(); + caClient.setCryptoSuite(cryptoSuite); + + // Create a wallet for managing identities + Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); + + // Check to see if we've already enrolled the user. + if (wallet.get("appUser") != null) { + System.out.println("An identity for the user \"appUser\" already exists in the wallet"); + return; + } + + X509Identity adminIdentity = (X509Identity)wallet.get("admin"); + if (adminIdentity == null) { + System.out.println("\"admin\" needs to be enrolled and added to the wallet first"); + return; + } + User admin = new User() { + + @Override + public String getName() { + return "admin"; + } + + @Override + public Set getRoles() { + return null; + } + + @Override + public String getAccount() { + return null; + } + + @Override + public String getAffiliation() { + return "org1.department1"; + } + + @Override + public Enrollment getEnrollment() { + return new Enrollment() { + + @Override + public PrivateKey getKey() { + return adminIdentity.getPrivateKey(); + } + + @Override + public String getCert() { + return Identities.toPemString(adminIdentity.getCertificate()); + } + }; + } + + @Override + public String getMspId() { + return "Org1MSP"; + } + + }; + + // Register the user, enroll the user, and import the new identity into the wallet. + RegistrationRequest registrationRequest = new RegistrationRequest("appUser"); + registrationRequest.setAffiliation("org1.department1"); + registrationRequest.setEnrollmentID("appUser"); + String enrollmentSecret = caClient.register(registrationRequest, admin); + Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret); + Identity user = Identities.newX509Identity("Org1MSP", enrollment); + wallet.put("appUser", user); + System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet"); + } + +} diff --git a/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties b/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties new file mode 100644 index 00000000..f1f841fe --- /dev/null +++ b/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties @@ -0,0 +1,19 @@ +# initialize root logger with level ERROR for stdout and fout +log4j.rootLogger=ERROR,stdout,fout +# set the log level for these components +log4j.logger.com.endeca=INFO +log4j.logger.com.endeca.itl.web.metrics=INFO + +# add a ConsoleAppender to the logger stdout to write to the console +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +# use a simple message format +log4j.appender.stdout.layout.ConversionPattern=%m%n + +# add a FileAppender to the logger fout +log4j.appender.fout=org.apache.log4j.FileAppender +# create a log file +log4j.appender.fout.File=crawl.log +log4j.appender.fout.layout=org.apache.log4j.PatternLayout +# use a more detailed message pattern +log4j.appender.fout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n From e90554f4f86b5199683e772197fca8e878ca7f41 Mon Sep 17 00:00:00 2001 From: Sijo Cherian Date: Thu, 6 Aug 2020 14:23:11 -0400 Subject: [PATCH 44/45] JS App workflow for private-data asset transfer, Chaincode fixes (#252) * Adding JS App workflow, Bugfixes in Go Chaincode Initial impl JS App workflow, Added 2 asset sample, with transfer & delete Bugfixes: Bug fix in ReadTransferAgreement Improved logging in Go Chaincode, Improved Chaincode error handling, checks for execution on orgs peer, bug fix, lint issues use addDiscoveryInterest to scope policy, instead of setEndorsingOrganizations Signed-off-by: Sijo Cherian * added js app to CI pipeline , and linting matrix Signed-off-by: Sijo Cherian Co-authored-by: Sijo Cherian --- .../application-javascript/.eslintignore | 5 + .../application-javascript/.eslintrc.js | 37 +++ .../application-javascript/.gitignore | 16 + .../application-javascript/app.js | 273 ++++++++++++++++++ .../application-javascript/caUtils.js | 97 +++++++ .../application-javascript/package.json | 45 +++ .../chaincode-go/go.mod | 18 +- .../chaincode-go/go.sum | 96 ++++++ .../chaincode-go/private_asset_queries.go | 80 ++--- .../chaincode-go/private_asset_transfer.go | 171 +++++++---- ci/azure-pipelines.yml | 8 +- ci/scripts/run-test-network-private.sh | 12 +- 12 files changed, 766 insertions(+), 92 deletions(-) create mode 100644 asset-transfer-private-data/application-javascript/.eslintignore create mode 100644 asset-transfer-private-data/application-javascript/.eslintrc.js create mode 100644 asset-transfer-private-data/application-javascript/.gitignore create mode 100644 asset-transfer-private-data/application-javascript/app.js create mode 100644 asset-transfer-private-data/application-javascript/caUtils.js create mode 100644 asset-transfer-private-data/application-javascript/package.json diff --git a/asset-transfer-private-data/application-javascript/.eslintignore b/asset-transfer-private-data/application-javascript/.eslintignore new file mode 100644 index 00000000..15958470 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/.eslintignore @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +coverage diff --git a/asset-transfer-private-data/application-javascript/.eslintrc.js b/asset-transfer-private-data/application-javascript/.eslintrc.js new file mode 100644 index 00000000..8b83df73 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/.eslintrc.js @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +module.exports = { + env: { + node: true, + mocha: true + }, + parserOptions: { + ecmaVersion: 8, + sourceType: 'script' + }, + extends: "eslint:recommended", + rules: { + indent: ['error', 4], + '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-tabs': '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-private-data/application-javascript/.gitignore b/asset-transfer-private-data/application-javascript/.gitignore new file mode 100644 index 00000000..b7db0913 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/.gitignore @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +wallet.org1 +wallet.org2 +!wallet.org1/.gitkeep +!wallet.org2/.gitkeep \ No newline at end of file diff --git a/asset-transfer-private-data/application-javascript/app.js b/asset-transfer-private-data/application-javascript/app.js new file mode 100644 index 00000000..93556f96 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/app.js @@ -0,0 +1,273 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const { Gateway, Wallets } = require('fabric-network'); +const FabricCAServices = require('fabric-ca-client'); + +const path = require('path'); +const fs = require('fs'); +const caUtils = require('./caUtils'); + +const myChannel = 'mychannel'; +const myChaincodeName = 'private'; + +const memberAssetCollectionName = 'assetCollection'; +const org1PrivateCollectionName = 'Org1MSPPrivateCollection'; +const org2PrivateCollectionName = 'Org2MSPPrivateCollection'; +const mspOrg2 = 'Org2MSP'; +const mspOrg1 = 'Org1MSP'; +function prettyJSONString(inputString) { + if (inputString) { + return JSON.stringify(JSON.parse(inputString), null, 2); + } + else { + return inputString; + } +} + +async function initContractFromOrg1Identity() { + console.log('\nFabric client user & Gateway init: Using Org1 identity to Org1 Peer'); + // load the network configuration + let ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + let fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + let ccpOrg1 = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + // Create a new file system based wallet for managing identities. + const walletPathOrg1 = path.join(__dirname, 'wallet/org1'); + const walletOrg1 = await Wallets.newFileSystemWallet(walletPathOrg1); + console.log(`Org1 wallet path: ${walletPathOrg1}`); + + // Create a new CA client for interacting with this Orgs CA. + const caInfo = ccpOrg1.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const caService = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + // register & enroll admin user with CA, stores admin identity in local wallet + await caUtils.EnrollOrgAdminUser(mspOrg1, walletOrg1, caService); + + // register & enroll application user with CA, which is used as client identify to make chaincode calls, stores app user identity in local wallet + await caUtils.RegisterOrgUser(caUtils.Org1UserId, mspOrg1, walletOrg1, caService); + + try { + // Create a new gateway for connecting to Org's peer node. + const gatewayOrg1 = new Gateway(); + //connect using Discovery enabled + await gatewayOrg1.connect(ccpOrg1, + { wallet: walletOrg1, identity: caUtils.Org1UserId, discovery: { enabled: true, asLocalhost: true } }); + + return gatewayOrg1; + } catch (error) { + console.error(`Error in connecting to gateway: ${error}`); + process.exit(1); + } +} + +async function initContractFromOrg2Identity() { + console.log('\nFabric client user & Gateway init: Using Org2 identity to Org2 Peer'); + // load the network configuration + let ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org2.example.com', 'connection-org2.json'); + let fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccpOrg2 = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPathOrg2 = path.join(__dirname, 'wallet/org2'); + const walletOrg2 = await Wallets.newFileSystemWallet(walletPathOrg2); + console.log(`Org2 wallet path: ${walletPathOrg2}`); + + // Create a new CA client for interacting with this Orgs CA. + const caInfo = ccpOrg2.certificateAuthorities['ca.org2.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const caService = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + await caUtils.EnrollOrgAdminUser(mspOrg2, walletOrg2, caService); + + // register & enroll application user with CA, which is used as client identify to make chaincode calls, stores app user identity in local wallet + await caUtils.RegisterOrgUser(caUtils.Org2UserId, mspOrg2, walletOrg2, caService); + + try { + // Create a new gateway for connecting to Org's peer node. + const gatewayOrg2 = new Gateway(); + await gatewayOrg2.connect(ccpOrg2, + { wallet: walletOrg2, identity: caUtils.Org2UserId, discovery: { enabled: true, asLocalhost: true } }); + + return gatewayOrg2; + } catch (error) { + console.error(`Error in connecting to gateway: ${error}`); + process.exit(1); + } +} + +// Main workflow : usecase details at asset-transfer-private-data/chaincode-go/README.md +// This app uses fabric-samples/test-network based setup and the companion chaincode +// For this usecase illustration, we will use both Org1 & Org2 client identity from this same app +// In real world the Org1 & Org2 identity will be used in different apps to achieve asset transfer. +async function main() { + try { + + /** ******* Fabric client init: Using Org1 identity to Org1 Peer ********** */ + const gatewayOrg1 = await initContractFromOrg1Identity(); + const networkOrg1 = await gatewayOrg1.getNetwork(myChannel); + const contractOrg1 = networkOrg1.getContract(myChaincodeName); + // Since this sample chaincode uses, Private Data Collection level endorsement policy, addDiscoveryInterest + // scopes the discovery service further to use the endorsement policies of collections, if any + contractOrg1.addDiscoveryInterest({ name: myChaincodeName, collectionNames: [memberAssetCollectionName, org1PrivateCollectionName] }); + + /** ~~~~~~~ Fabric client init: Using Org2 identity to Org2 Peer ~~~~~~~ */ + const gatewayOrg2 = await initContractFromOrg2Identity(); + const networkOrg2 = await gatewayOrg2.getNetwork(myChannel); + const contractOrg2 = networkOrg2.getContract(myChaincodeName); + contractOrg2.addDiscoveryInterest({ name: myChaincodeName, collectionNames: [memberAssetCollectionName, org2PrivateCollectionName] }); + try { + // Sample transactions are listed below + // Add few sample Assets & transfers one of the asset from Org1 to Org2 as the new owner + let assetID1 = 'asset1'; + let assetID2 = 'asset2'; + const assetType = 'ValuableAsset'; + let result; + let asset1Data = { objectType: assetType, assetID: assetID1, color: 'green', size: 20, appraisedValue: 100 }; + 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); + let statefulTxn = contractOrg1.createTransaction('CreateAsset'); + //if you need to customize endorsement to specific set of Orgs, use setEndorsingOrganizations + //statefulTxn.setEndorsingOrganizations(mspOrg1); + let tmapData = Buffer.from(JSON.stringify(asset1Data)); + statefulTxn.setTransient({ + asset_properties: tmapData + }); + result = await statefulTxn.submit(); + + //Add asset2 + console.log('Submit Transaction: CreateAsset ' + assetID2); + statefulTxn = contractOrg1.createTransaction('CreateAsset'); + tmapData = Buffer.from(JSON.stringify(asset2Data)); + statefulTxn.setTransient({ + asset_properties: tmapData + }); + result = await statefulTxn.submit(); + + console.log('\n***********************'); + console.log('Evaluate Transaction: GetAssetByRange asset0-asset9'); + // GetAssetByRange returns assets on the ledger with ID in the range of startKey (inclusive) and endKey (exclusive) + result = await contractOrg1.evaluateTransaction('GetAssetByRange', 'asset0', 'asset9'); + console.log(' result: ' + prettyJSONString(result.toString())); + + console.log('\n***********************'); + console.log('Evaluate Transaction: ReadAssetPrivateDetails from ' + org1PrivateCollectionName); + // ReadAssetPrivateDetails reads data from Org's private collection. Args: collectionName, assetID + result = await contractOrg1.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + + + console.log('\n~~~~~~~~~~~~~~~~ As Org2 Client ~~~~~~~~~~~~~~~~'); + console.log('Evaluate Transaction: ReadAsset ' + assetID1); + result = await contractOrg2.evaluateTransaction('ReadAsset', assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + let assetOwner = JSON.parse(result.toString()).owner; + console.log(' Asset owner: ' + Buffer.from(assetOwner, 'base64').toString()); + + // Org2 cannot ReadAssetPrivateDetails from Org1's private collection due to Collection policy + // Will fail: await contractOrg2.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1); + + // Buyer from Org2 agrees to buy the asset assetID1 // + // To purchase the asset, the buyer needs to agree to the same value as the asset owner + let dataForAgreement = { assetID: assetID1, appraisedValue: 100 }; + console.log('\nSubmit Transaction: AgreeToTransfer payload ' + JSON.stringify(dataForAgreement)); + statefulTxn = contractOrg2.createTransaction('AgreeToTransfer'); + tmapData = Buffer.from(JSON.stringify(dataForAgreement)); + statefulTxn.setTransient({ + asset_value: tmapData + }); + result = await statefulTxn.submit(); + + //Buyer can withdraw the Agreement, using DeleteTranferAgreement + /*statefulTxn = contractOrg2.createTransaction('DeleteTranferAgreement'); + statefulTxn.setEndorsingOrganizations(mspOrg2); + let dataForDeleteAgreement = { assetID: assetID1 }; + tmapData = Buffer.from(JSON.stringify(dataForDeleteAgreement)); + statefulTxn.setTransient({ + agreement_delete: tmapData + }); + result = await statefulTxn.submit();*/ + + console.log('\n**************** As Org1 Client ****************'); + // All members can send txn ReadTransferAgreement, set by Org2 above + console.log('Evaluate Transaction: ReadTransferAgreement ' + assetID1); + result = await contractOrg1.evaluateTransaction('ReadTransferAgreement', assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + + // Transfer the asset to Org2 // + // To transfer the asset, the owner needs to pass the MSP ID of new asset owner, and initiate the transfer + console.log('Submit Transaction: TransferAsset ' + assetID1); + let buyerDetails = { assetID: assetID1, buyerMSP: mspOrg2 }; + statefulTxn = contractOrg1.createTransaction('TransferAsset'); + tmapData = Buffer.from(JSON.stringify(buyerDetails)); + statefulTxn.setTransient({ + asset_owner: tmapData + }); + result = await statefulTxn.submit(); + + console.log('\n***********************'); + //Again ReadAsset : results will show that the buyer identity now owns the asset: + console.log('Evaluate Transaction: ReadAsset ' + assetID1); + result = await contractOrg1.evaluateTransaction('ReadAsset', assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + assetOwner = JSON.parse(result.toString()).owner; + console.log(' Asset owner: ' + Buffer.from(assetOwner, 'base64').toString()); + + //Confirm that transfer removed the private details from the Org1 collection: + console.log('Evaluate Transaction: ReadAssetPrivateDetails'); + // ReadAssetPrivateDetails reads data from Org's private collection: Should return empty + result = await contractOrg1.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + + console.log('Evaluate Transaction: ReadAsset ' + assetID2); + result = await contractOrg1.evaluateTransaction('ReadAsset', assetID2); + console.log(' result: ' + prettyJSONString(result.toString())); + + console.log('\n***********************'); + // Delete Asset2 + console.log('Deleting Asset ' + assetID2); + statefulTxn = contractOrg1.createTransaction('DeleteAsset'); + + let dataForDelete = { assetID: assetID2 }; + tmapData = Buffer.from(JSON.stringify(dataForDelete)); + statefulTxn.setTransient({ + asset_delete: tmapData + }); + result = await statefulTxn.submit(); + console.log('Evaluate Transaction: ReadAsset ' + assetID2); + result = await contractOrg1.evaluateTransaction('ReadAsset', assetID2); + console.log(' result: ' + prettyJSONString(result.toString())); + + console.log('\n~~~~~~~~~~~~~~~~ As Org2 Client ~~~~~~~~~~~~~~~~'); + // Org2 can ReadAssetPrivateDetails: Org2 is owner, and private details exist in new owner's Collection + console.log('Evaluate Transaction as Org2: ReadAssetPrivateDetails ' + assetID1 + ' from ' + org2PrivateCollectionName); + result = await contractOrg2.evaluateTransaction('ReadAssetPrivateDetails', org2PrivateCollectionName, assetID1); + console.log(' result: ' + prettyJSONString(result.toString())); + } finally { + // Disconnect from the gateway peer when all work for this client identity is complete + gatewayOrg1.disconnect(); + gatewayOrg2.disconnect(); + } + } catch (error) { + console.error(`Error in transaction: ${error}`); + if (error.stack) { + console.error(error.stack); + } + process.exit(1); + } +} + +main(); \ No newline at end of file diff --git a/asset-transfer-private-data/application-javascript/caUtils.js b/asset-transfer-private-data/application-javascript/caUtils.js new file mode 100644 index 00000000..645a8378 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/caUtils.js @@ -0,0 +1,97 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const adminUserId = 'admin'; +const adminUserPasswd = 'adminpw'; +const org1UserId = 'appUser1'; +const org2UserId = 'appUser2'; +const caChaincodeUserRole = 'client'; + +async function registerOrgUser(appUserId, mspId, wallet, caService) { + try { + // Check to see if we've already enrolled the user. + const userIdentity = await wallet.get(appUserId); + if (userIdentity) { + console.log('An identity for the user ' + appUserId + ' already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminIdentity = await wallet.get(adminUserId); + if (!adminIdentity) { + console.log('An identity for the admin user does not exist in the wallet'); + console.log('Call enrollAdmin for admin user enroll before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, adminUserId); + + // Register the user, enroll the user, and import the new identity into the wallet. + // if affiliation is specified by client, the affiliation value must be configured in CA + const secret = await caService.register({ + affiliation: 'org2.department1', + enrollmentID: appUserId, + role: caChaincodeUserRole + }, adminUser); + const enrollment = await caService.enroll({ + enrollmentID: appUserId, + enrollmentSecret: secret + }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: mspId, + type: 'X.509', + }; + await wallet.put(appUserId, x509Identity); + console.log('Successfully registered and enrolled user ' + appUserId + ' and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user : ${error}`); + process.exit(1); + } +} + +async function enrollOrgAdminUser(mspId, wallet, caService) { + try { + + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get(adminUserId); + if (identity) { + console.log('An identity for the admin user already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await caService.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: mspId, + type: 'X.509', + }; + await wallet.put(adminUserId, x509Identity); + console.log('Successfully enrolled admin user and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user : ${error}`); + process.exit(1); + } +} + +exports.AdminUserId = adminUserId; +exports.Org1UserId = org1UserId; +exports.Org2UserId = org2UserId; +exports.RegisterOrgUser = registerOrgUser; +exports.EnrollOrgAdminUser = enrollOrgAdminUser; \ No newline at end of file diff --git a/asset-transfer-private-data/application-javascript/package.json b/asset-transfer-private-data/application-javascript/package.json new file mode 100644 index 00000000..265ceed8 --- /dev/null +++ b/asset-transfer-private-data/application-javascript/package.json @@ -0,0 +1,45 @@ +{ + "name": "asset-transfer-private-data", + "version": "1.0.0", + "description": "asset-transfer-private-data application implemented in JavaScript", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "scripts": { + "lint": "eslint .", + "pretest": "npm run lint", + "test": "nyc mocha --recursive" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.2.0", + "fabric-network": "^2.2.0" + }, + "devDependencies": { + "chai": "^4.2.0", + "eslint": "^5.9.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0" + }, + "nyc": { + "exclude": [ + "coverage/**", + "test/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/asset-transfer-private-data/chaincode-go/go.mod b/asset-transfer-private-data/chaincode-go/go.mod index f895d27d..332f10af 100644 --- a/asset-transfer-private-data/chaincode-go/go.mod +++ b/asset-transfer-private-data/chaincode-go/go.mod @@ -3,6 +3,22 @@ module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chainco go 1.14 require ( - github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 + github.com/go-openapi/jsonreference v0.19.4 // indirect + github.com/go-openapi/spec v0.19.8 // indirect + github.com/go-openapi/swag v0.19.9 // indirect + github.com/gobuffalo/envy v1.9.0 // indirect + github.com/gobuffalo/packd v1.0.0 // indirect + github.com/golang/protobuf v1.4.2 // indirect + github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a github.com/hyperledger/fabric-contract-api-go v1.1.0 + github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 // indirect + github.com/mailru/easyjson v0.7.1 // indirect + github.com/rogpeppe/go-internal v1.6.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect + golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 // indirect + google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 // indirect + google.golang.org/grpc v1.30.0 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/asset-transfer-private-data/chaincode-go/go.sum b/asset-transfer-private-data/chaincode-go/go.sum index 94a66455..f22cc6ed 100644 --- a/asset-transfer-private-data/chaincode-go/go.sum +++ b/asset-transfer-private-data/chaincode-go/go.sum @@ -6,48 +6,84 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= +github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE= +github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= +github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o= github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a h1:KoFw2HnRfW+EItMP0zvUUl1FGzDb/7O0ov7uXZffQok= +github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc= github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4= github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E= github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E= github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= +github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 h1:SEbB3yH4ISTGRifDamYXAst36gO2kM855ndMJlsv+pc= +github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -55,21 +91,30 @@ github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0L github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.0 h1:IZRgg4sfrDH7nsAD1Y/Nwj+GzIfEwpJSLjCaNC3SbsI= +github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -84,10 +129,13 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= @@ -97,15 +145,27 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -114,25 +174,61 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 h1:10ohwcLf82I55O/aQxYqmWKoOdNbQTYYComeP1KDOS4= +google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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 cf87cb25..ea834a82 100644 --- a/asset-transfer-private-data/chaincode-go/private_asset_queries.go +++ b/asset-transfer-private-data/chaincode-go/private_asset_queries.go @@ -9,6 +9,7 @@ package main import ( "encoding/json" "fmt" + "log" "github.com/hyperledger/fabric-contract-api-go/contractapi" ) @@ -16,16 +17,20 @@ import ( // ReadAsset reads the information from collection func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) { + log.Printf("ReadAsset: collection %v, ID %v", assetCollection, assetID) assetJSON, err := ctx.GetStub().GetPrivateData(assetCollection, assetID) //get the asset from chaincode state if err != nil { return nil, fmt.Errorf("failed to read from asset %v", err) } + + //No Asset found, return empty response if assetJSON == nil { - return nil, fmt.Errorf("%v does not exist", assetID) + log.Printf("%v does not exist in collection %v", assetID, assetCollection) + return nil, nil } - asset := new(Asset) - err = json.Unmarshal(assetJSON, asset) + var asset *Asset + err = json.Unmarshal(assetJSON, &asset) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %v", err) } @@ -36,17 +41,18 @@ func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, a // ReadAssetPrivateDetails reads the asset private details in organization specific collection func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionContextInterface, collection string, assetID string) (*AssetPrivateDetails, error) { - + log.Printf("ReadAssetPrivateDetails: collection %v, ID %v", collection, assetID) assetDetailsJSON, err := ctx.GetStub().GetPrivateData(collection, assetID) // Get the asset from chaincode state if err != nil { return nil, fmt.Errorf("failed to read from asset details %v", err) } if assetDetailsJSON == nil { - return nil, fmt.Errorf("appraisal value for %v does not exist in private data collection", assetID) + log.Printf("AssetPrivateDetails for %v does not exist in collection %v", assetID, collection) + return nil, nil } - assetDetails := new(AssetPrivateDetails) - err = json.Unmarshal(assetDetailsJSON, assetDetails) + var assetDetails *AssetPrivateDetails + err = json.Unmarshal(assetDetailsJSON, &assetDetails) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %v", err) } @@ -54,34 +60,34 @@ func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionConte return assetDetails, nil } -// ReadTransferAgreement gets the identity from the transfer agreement from collection -func (s *SmartContract) ReadTransferAgreement(ctx contractapi.TransactionContextInterface, assetID string) (string, error) { - - // create composite key +// ReadTransferAgreement gets the buyer's identity from the transfer agreement from collection +func (s *SmartContract) ReadTransferAgreement(ctx contractapi.TransactionContextInterface, assetID string) (*TransferAgreement, error) { + log.Printf("ReadTransferAgreement: collection %v, ID %v", assetCollection, assetID) + // composite key for TransferAgreement of this asset transferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetID}) if err != nil { - return "", fmt.Errorf("failed to create composite key: %v", err) + return nil, fmt.Errorf("failed to create composite key: %v", err) } buyerIdentity, err := ctx.GetStub().GetPrivateData(assetCollection, transferAgreeKey) // Get the identity from collection if err != nil { - return "", fmt.Errorf("failed to read from asset %v", err) + return nil, fmt.Errorf("failed to read TransferAgreement: %v", err) } if buyerIdentity == nil { - return "", fmt.Errorf("transfer agreement for %v does not exist", assetID) + log.Printf("TransferAgreement for %v does not exist", assetID) + return nil, nil } - - return string(buyerIdentity), nil - + agreement := &TransferAgreement{ + ID: assetID, + BuyerID: string(buyerIdentity), + } + return agreement, nil } -// =========================================================================================== // GetAssetByRange performs a range query based on the start and end keys provided. Range // queries can be used to read data from private data collections, but can not be used in // a transaction that also writes to private data. - -// =========================================================================================== -func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]Asset, error) { +func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]*Asset, error) { resultsIterator, err := ctx.GetStub().GetPrivateDataByRange(assetCollection, startKey, endKey) if err != nil { @@ -89,7 +95,7 @@ func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterf } defer resultsIterator.Close() - results := []Asset{} + results := []*Asset{} for resultsIterator.HasNext() { response, err := resultsIterator.Next() @@ -97,14 +103,13 @@ func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterf return nil, err } - asset := new(Asset) - - err = json.Unmarshal(response.Value, asset) + var asset *Asset + err = json.Unmarshal(response.Value, &asset) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %v", err) } - results = append(results, *asset) + results = append(results, asset) } return results, nil @@ -125,14 +130,15 @@ func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterf // ============================================================================================ // ===== Example: Parameterized rich query ================================================= -// QueryAssetByOwner queries for assets based on a passed in owner. + +// QueryAssetByOwner queries for assets based on assetType, 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) // ========================================================================================= -func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]Asset, error) { +func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInterface, assetType string, owner string) ([]*Asset, error) { - queryString := fmt.Sprintf("{\"selector\":{\"type\":\"asset\",\"owner\":\"%s\"}}", owner) + queryString := fmt.Sprintf("{\"selector\":{\"objectType\":\"%v\",\"owner\":\"%v\"}}", assetType, owner) queryResults, err := s.getQueryResultForQueryString(ctx, queryString) if err != nil { @@ -141,14 +147,13 @@ func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInte return queryResults, nil } -// ===== 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 QueryAssetByOwner example for parameterized queries. // Only available on state databases that support rich query (e.g. CouchDB) -// ========================================================================================= -func (s *SmartContract) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]Asset, error) { +func (s *SmartContract) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) { queryResults, err := s.getQueryResultForQueryString(ctx, queryString) if err != nil { @@ -158,7 +163,7 @@ func (s *SmartContract) QueryAssets(ctx contractapi.TransactionContextInterface, } // getQueryResultForQueryString executes the passed in query string. -func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]Asset, error) { +func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) { resultsIterator, err := ctx.GetStub().GetPrivateDataQueryResult(assetCollection, queryString) if err != nil { @@ -166,22 +171,21 @@ func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.Transaction } defer resultsIterator.Close() - results := []Asset{} + results := []*Asset{} for resultsIterator.HasNext() { response, err := resultsIterator.Next() if err != nil { return nil, err } + var asset *Asset - asset := new(Asset) - - err = json.Unmarshal(response.Value, asset) + err = json.Unmarshal(response.Value, &asset) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON: %v", err) } - results = append(results, *asset) + results = append(results, asset) } return results, nil } diff --git a/asset-transfer-private-data/chaincode-go/private_asset_transfer.go b/asset-transfer-private-data/chaincode-go/private_asset_transfer.go index 90434cdb..c3e51dd0 100644 --- a/asset-transfer-private-data/chaincode-go/private_asset_transfer.go +++ b/asset-transfer-private-data/chaincode-go/private_asset_transfer.go @@ -16,6 +16,9 @@ import ( "github.com/hyperledger/fabric-contract-api-go/contractapi" ) +const assetCollection = "assetCollection" +const transferAgreementObjectType = "transferAgreement" + // Asset describes main asset details that are visible to all organizations type Asset struct { Type string `json:"objectType"` //Type is used to distinguish the various types of objects in state database @@ -31,10 +34,13 @@ type AssetPrivateDetails struct { AppraisedValue int `json:"appraisedValue"` } -const assetCollection = "assetCollection" - -const transferAgreementObjectType = "transferAgreement" +// TransferAgreement describes the buyer agreement returned by ReadTransferAgreement +type TransferAgreement struct { + ID string `json:"assetID"` + BuyerID string `json:"buyerID"` +} +// SmartContract of this fabric sample type SmartContract struct { contractapi.Contract } @@ -49,10 +55,11 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) return fmt.Errorf("error getting transient: %v", err) } - // Asset properties are private, therefore they get passed in transient field + // Asset properties are private, therefore they get passed in transient field, instead of func args transientAssetJSON, ok := transientMap["asset_properties"] if !ok { - return fmt.Errorf("asset not found in the transient map") + //log error to stdout + return fmt.Errorf("asset not found in the transient map input") } type assetTransientInput struct { @@ -90,7 +97,7 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) if err != nil { return fmt.Errorf("failed to get asset: %v", err) } else if assetAsBytes != nil { - fmt.Println("this asset already exists: " + assetInput.ID) + fmt.Println("Asset already exists: " + assetInput.ID) return fmt.Errorf("this asset already exists: " + assetInput.ID) } @@ -100,6 +107,14 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) return fmt.Errorf("failed to get verified OrgID: %v", err) } + // Verify that the client is submitting request to peer in their organization + // This is to ensure that a client from another org doesn't attempt to read or + // write private data from this peer. + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("CreateAsset cannot be performed: Error %v", err) + } + // Make submitting client the owner asset := &Asset{ Type: assetInput.Type, @@ -110,13 +125,16 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) } assetJSONasBytes, err := json.Marshal(asset) if err != nil { - return fmt.Errorf("failed to marshal into JSON: %v", err) + return fmt.Errorf("failed to marshal asset into JSON: %v", err) } // Save asset to private data collection + // Typical logger, logs to stdout/file in the fabric managed docker container, running this chaincode + // Look for container name like dev-peer0.org1.example.com-{chaincodename_version}-xyz + log.Printf("CreateAsset Put: collection %v, ID %v", assetCollection, assetInput.ID) err = ctx.GetStub().PutPrivateData(assetCollection, assetInput.ID, assetJSONasBytes) if err != nil { - return fmt.Errorf("failed to put asset into private data collection: %v", err) + return fmt.Errorf("failed to put asset into private data collecton: %v", err) } // Save asset details to collection visible to owning organization @@ -130,10 +148,14 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) return fmt.Errorf("failed to marshal into JSON: %v", err) } - // Get collection name for this organization. Needs to be read by a member of the organization. - orgCollection, err := getCollectionName(ctx, true) + // Get collection name for this organization. + orgCollection, err := getCollectionName(ctx) + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } // Put asset appraised value into owners org specific private data collection + log.Printf("Put: collection %v, ID %v", orgCollection, assetInput.ID) err = ctx.GetStub().PutPrivateData(orgCollection, assetInput.ID, assetPrivateDetailsAsBytes) if err != nil { return fmt.Errorf("failed to put asset private details: %v", err) @@ -180,9 +202,19 @@ func (s *SmartContract) AgreeToTransfer(ctx contractapi.TransactionContextInterf return fmt.Errorf("appraisedValue field must be a positive integer") } - // Get collection name for this organization. Needs to be read by a member of the organization. - orgCollection, err := getCollectionName(ctx, true) + // Verify that the client is submitting request to peer in their organization + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("AgreeToTransfer cannot be performed: Error %v", err) + } + // Get collection name for this organization. Needs to be read by a member of the organization. + orgCollection, err := getCollectionName(ctx) + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } + + log.Printf("AgreeToTransfer Put: collection %v, ID %v", orgCollection, valueJSON.ID) // Put agreed value in the org specifc private data collection err = ctx.GetStub().PutPrivateData(orgCollection, valueJSON.ID, valueJSONasBytes) if err != nil { @@ -197,6 +229,7 @@ func (s *SmartContract) AgreeToTransfer(ctx contractapi.TransactionContextInterf return fmt.Errorf("failed to create composite key: %v", err) } + log.Printf("AgreeToTransfer Put: collection %v, ID %v, Key %v", assetCollection, valueJSON.ID, transferAgreeKey) err = ctx.GetStub().PutPrivateData(assetCollection, transferAgreeKey, []byte(clientID)) if err != nil { return fmt.Errorf("failed to put asset bid: %v", err) @@ -236,34 +269,54 @@ func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterfac if len(assetTransferInput.BuyerMSP) == 0 { return fmt.Errorf("buyerMSP field must be a non-empty string") } - + log.Printf("TransferAsset: verify asset exists ID %v", assetTransferInput.ID) // Read asset from the private data collection asset, err := s.ReadAsset(ctx, assetTransferInput.ID) if err != nil { return fmt.Errorf("failed to get asset: %v", err) } + // Verify that the client is submitting request to peer in their organization + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("TransferAsset cannot be performed: Error %v", err) + } + // Verify transfer details and transfer owner err = s.verifyAgreement(ctx, assetTransferInput.ID, asset.Owner, assetTransferInput.BuyerMSP) if err != nil { return fmt.Errorf("failed transfer verification: %v", err) } - buyerID, err := s.ReadTransferAgreement(ctx, assetTransferInput.ID) + transferAgreement, err := s.ReadTransferAgreement(ctx, assetTransferInput.ID) + if err != nil { + return fmt.Errorf("failed ReadTransferAgreement to find buyerID: %v", err) + } + if transferAgreement.BuyerID == "" { + return fmt.Errorf("BuyerID not found in TransferAgreement for %v", assetTransferInput.ID) + } // Transfer asset in private data collection to new owner - asset.Owner = buyerID + asset.Owner = transferAgreement.BuyerID - assetJSONasBytes, _ := json.Marshal(asset) + assetJSONasBytes, err := json.Marshal(asset) + if err != nil { + return fmt.Errorf("failed marshalling asset %v: %v", assetTransferInput.ID, err) + } + + log.Printf("TransferAsset Put: collection %v, ID %v", assetCollection, assetTransferInput.ID) err = ctx.GetStub().PutPrivateData(assetCollection, assetTransferInput.ID, assetJSONasBytes) //rewrite the asset if err != nil { return err } // Get collection name for this organization - ownersCollection, err := getCollectionName(ctx, false) + ownersCollection, err := getCollectionName(ctx) + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } - // Delete the marble appraised value from this organiztion's private data collection + // Delete the asset appraised value from this organization's private data collection err = ctx.GetStub().DelPrivateData(ownersCollection, assetTransferInput.ID) if err != nil { return err @@ -304,8 +357,10 @@ func (s *SmartContract) verifyAgreement(ctx contractapi.TransactionContextInterf // Check 2: verify that the buyer has agreed to the appraised value // Get collection names - - collectionOwner, err := getCollectionName(ctx, false) // get buyers collection + collectionOwner, err := getCollectionName(ctx) // get owner collection from caller identity + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } collectionBuyer := buyerMSP + "PrivateCollection" // get buyers collection @@ -324,7 +379,7 @@ func (s *SmartContract) verifyAgreement(ctx contractapi.TransactionContextInterf return fmt.Errorf("failed to get hash of appraised value from buyer collection %v: %v", collectionBuyer, err) } if buyerAppraisedValueHash == nil { - return fmt.Errorf("hash of appraised value for %v does not exist in collection %v", assetID, collectionBuyer) + return fmt.Errorf("hash of appraised value for %v does not exist in collection %v. AgreeToTransfer must be called by the buyer first", assetID, collectionBuyer) } // Verify that the two hashes match @@ -363,12 +418,19 @@ func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface) return fmt.Errorf("assetID field must be a non-empty string") } + // Verify that the client is submitting request to peer in their organization + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("DeleteAsset cannot be performed: Error %v", err) + } + + log.Printf("Deleting Asset: %v", assetDeleteInput.ID) valAsbytes, err := ctx.GetStub().GetPrivateData(assetCollection, assetDeleteInput.ID) //get the asset from chaincode state if err != nil { return fmt.Errorf("failed to read asset: %v", err) } if valAsbytes == nil { - return fmt.Errorf("asset private details does not exist: %v", assetDeleteInput.ID) + return fmt.Errorf("asset not found: %v", assetDeleteInput.ID) } var assetToDelete Asset @@ -384,8 +446,10 @@ func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface) } // Finally, delete private details of asset - - ownerCollection, err := getCollectionName(ctx, true) // Get owners collection. Needs to be read by a member of the organization. + ownerCollection, err := getCollectionName(ctx) // Get owners collection + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } err = ctx.GetStub().DelPrivateData(ownerCollection, assetDeleteInput.ID) // Delete the asset if err != nil { @@ -406,7 +470,7 @@ func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContex } // Asset properties are private, therefore they get passed in transient field - transientDeleteJSON, ok := transientMap["agree_delete"] + transientDeleteJSON, ok := transientMap["agreement_delete"] if !ok { return fmt.Errorf("asset to delete not found in the transient map") } @@ -425,23 +489,38 @@ func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContex return fmt.Errorf("ID field must be a non-empty string") } + // Verify that the client is submitting request to peer in their organization + err = verifyClientOrgMatchesPeerOrg(ctx) + if err != nil { + return fmt.Errorf("DeleteTranferAgreement cannot be performed: Error %v", err) + } // Delete private details of agreement + orgCollection, err := getCollectionName(ctx) // Get proposers collection. + if err != nil { + return fmt.Errorf("failed to infer private collection name for the org: %v", err) + } + tranferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetDeleteInput. + ID}) // Create composite key + if err != nil { + return fmt.Errorf("failed to create composite key: %v", err) + } - orgCollection, err := getCollectionName(ctx, true) // Get proposers collection. Needs to be read by a member of the organization. + valAsbytes, err := ctx.GetStub().GetPrivateData(assetCollection, tranferAgreeKey) //get the transfer_agreement + if err != nil { + return fmt.Errorf("failed to read transfer_agreement: %v", err) + } + if valAsbytes == nil { + return fmt.Errorf("asset's transfer_agreement does not exist: %v", assetDeleteInput.ID) + } + log.Printf("Deleting TranferAgreement: %v", assetDeleteInput.ID) err = ctx.GetStub().DelPrivateData(orgCollection, assetDeleteInput.ID) // Delete the asset if err != nil { return err } // Delete transfer agreement record - - tranferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetDeleteInput.ID}) // Create composite key - if err != nil { - return fmt.Errorf("failed to create composite key: %v", err) - } - - err = ctx.GetStub().DelState(tranferAgreeKey) // remove agreement from state + err = ctx.GetStub().DelPrivateData(assetCollection, tranferAgreeKey) // remove agreement from state if err != nil { return err } @@ -451,22 +530,12 @@ func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContex } // getCollectionName is an internal helper function to get collection of submitting client identity. -// The collection name can optionally be verified against the peer org ID, to ensure that a -// client from another org doesn't attempt to read or write private data from this peer. -func getCollectionName(ctx contractapi.TransactionContextInterface, verifyOrg bool) (string, error) { +func getCollectionName(ctx contractapi.TransactionContextInterface) (string, error) { // Get the MSP ID of submitting client identity clientMSPID, err := ctx.GetClientIdentity().GetMSPID() if err != nil { - return "", fmt.Errorf("failed to get verified OrgID: %v", err) - } - - // Verify that the client is submitting request to peer in their organization - if verifyOrg { - err = verifyClientOrgMatchesPeerOrg(clientMSPID) - if err != nil { - return "", err - } + return "", fmt.Errorf("failed to get verified MSPID: %v", err) } // Create the collection name @@ -476,10 +545,14 @@ func getCollectionName(ctx contractapi.TransactionContextInterface, verifyOrg bo } // verifyClientOrgMatchesPeerOrg is an internal function used verify client org id and matches peer org id. -func verifyClientOrgMatchesPeerOrg(clientMSPID string) error { +func verifyClientOrgMatchesPeerOrg(ctx contractapi.TransactionContextInterface) error { + clientMSPID, err := ctx.GetClientIdentity().GetMSPID() + if err != nil { + return fmt.Errorf("failed getting the client's MSPID: %v", err) + } peerMSPID, err := shim.GetMSPID() if err != nil { - return fmt.Errorf("failed getting peer's orgID: %v", err) + return fmt.Errorf("failed getting the peer's MSPID: %v", err) } if clientMSPID != peerMSPID { @@ -494,11 +567,11 @@ func main() { chaincode, err := contractapi.NewChaincode(new(SmartContract)) if err != nil { - log.Panicf("error creating private mables chaincode: %v", err) + log.Panicf("error creating the chaincode: %v", err) return } if err := chaincode.Start(); err != nil { - log.Panicf("error starting private mables chaincode: %v", err) + log.Panicf("error starting the chaincode: %v", err) } } diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 759d36a3..bd9c3d9c 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -99,11 +99,15 @@ jobs: Ledger-Chaincode-Go: DIRECTORY: asset-transfer-ledger-queries LANGUAGE: go - TYPE: chaincode - Private-Chaincode-Go: + TYPE: chaincode + PrivateData-Chaincode-Go: DIRECTORY: asset-transfer-private-data LANGUAGE: go TYPE: chaincode + PrivateData-Application-Javascript: + DIRECTORY: asset-transfer-private-data + LANGUAGE: javascript + TYPE: application Secured-Chaincode-Go: DIRECTORY: asset-transfer-secured-agreement LANGUAGE: go diff --git a/ci/scripts/run-test-network-private.sh b/ci/scripts/run-test-network-private.sh index c2578c40..c38cf978 100755 --- a/ci/scripts/run-test-network-private.sh +++ b/ci/scripts/run-test-network-private.sh @@ -14,8 +14,16 @@ function print() { print "Creating network" ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}" -print "Deploying ${CHAINCODE_NAME} chaincode" -./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" +print "Deploying 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 + +# 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 From c0ffb1cffb6d7d953cf1d34214f54090c4822973 Mon Sep 17 00:00:00 2001 From: harrisob Date: Thu, 6 Aug 2020 16:07:54 -0400 Subject: [PATCH 45/45] Restructure the basic javascript application to use common code. (#281) Use common code for reading a connection profile, enrolling the admin, registering and enrolling a user, building a wallet, and building a certificate authority client. Signed-off-by: Bret Harrison --- .../application-javascript/.eslintrc.js | 3 +- .../application-javascript/app.js | 212 ++++++++++-------- .../application-javascript/enrollAdmin.js | 62 ----- .../application-javascript/package.json | 29 --- .../application-javascript/registerUser.js | 83 ------- test-application/javascript/AppUtil.js | 40 ++++ test-application/javascript/CAUtil.js | 101 +++++++++ 7 files changed, 259 insertions(+), 271 deletions(-) delete mode 100644 asset-transfer-basic/application-javascript/enrollAdmin.js delete mode 100644 asset-transfer-basic/application-javascript/registerUser.js create mode 100644 test-application/javascript/AppUtil.js create mode 100644 test-application/javascript/CAUtil.js diff --git a/asset-transfer-basic/application-javascript/.eslintrc.js b/asset-transfer-basic/application-javascript/.eslintrc.js index 8b83df73..6fa636ba 100644 --- a/asset-transfer-basic/application-javascript/.eslintrc.js +++ b/asset-transfer-basic/application-javascript/.eslintrc.js @@ -13,7 +13,7 @@ module.exports = { }, extends: "eslint:recommended", rules: { - indent: ['error', 4], + indent: ['error', 'tab'], 'linebreak-style': ['error', 'unix'], quotes: ['error', 'single'], semi: ['error', 'always'], @@ -25,7 +25,6 @@ module.exports = { strict: 'error', 'no-var': 'error', 'dot-notation': 'error', - 'no-tabs': 'error', 'no-trailing-spaces': 'error', 'no-use-before-define': 'error', 'no-useless-call': 'error', diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js index e044991f..b060133a 100644 --- a/asset-transfer-basic/application-javascript/app.js +++ b/asset-transfer-basic/application-javascript/app.js @@ -7,127 +7,149 @@ 'use strict'; const {Gateway, Wallets} = require('fabric-network'); +const FabricCAServices = require('fabric-ca-client'); const path = require('path'); -const fs = require('fs'); -const registerUser = require('./registerUser'); -const enrollAdmin = require('./enrollAdmin'); +const {buildCAClient, registerUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js'); +const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js'); -const myChannel = 'mychannel'; -const myChaincodeName = 'basic'; +const channelName = 'mychannel'; +const chaincodeName = 'basic'; +const walletPath = path.join(__dirname, 'wallet'); +const userId = 'appUser'; function prettyJSONString(inputString) { - return JSON.stringify(JSON.parse(inputString), null, 2); + return JSON.stringify(JSON.parse(inputString), null, 2); } // pre-requisites: -// fabric-sample test-network setup with two peers and an ordering service, -// the companion chaincode is deployed, approved and committed on the channel mychannel +// - fabric-sample two organization test-network setup with two peers, ordering service, and 2 certificate authorities +// ===> from directory /fabric-samples/test-network +// network.sh run createChannel -ca +// - any of the asset-transfer-basic chaincodes deployed on the channel "mychannel" with the chaincodeName of "basic" +// This deploy command will package, install, approve, and commit the javascript chaincode, all the actions it takes +// to deploy a chaincode to a channel. +// ===> from directory /fabric-samples/test-network +// network.sh deployCC -ccn basic -ccl javascript +// - node install +// - npm installed code dependencies +// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript +// npm install +// - to run this test application +// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript +// node app.js +// # this may be run again again + +/** + * A test application to show basic operations with any of the asset-transfer-basic chaincodes + * -- How to submit a transaction + * -- How to query and check the results + * + * To see the SDK workings, try setting the logging to show on the console before running + * export HFC_LOGGING='{"debug":"console"}' + */ async function main() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + try { + // build an in memory object with the network configuration (also known as a connection profile) + const ccp = buildCCP(); - // Create a new file system based wallet for managing identities. - const walletPath = path.join(__dirname, 'wallet'); - const wallet = await Wallets.newFileSystemWallet(walletPath); - console.log(`Wallet path: ${walletPath}`); + // build an instance of the fabric ca services client based on + // the information in the network configuration + const caClient = buildCAClient(FabricCAServices, ccp); + // setup the wallet to hold the credentials of the application user + const wallet = await buildWallet(Wallets, walletPath); - // Steps: - // Note: Steps 1 & 2 need to done only once in an app-server per blockchain network - // 1. register & enroll admin user with CA, stores admin identity in local wallet - await enrollAdmin.EnrollAdminUser(); + // in a real application this would be done on an administrative flow, and only once + await enrollAdmin(caClient, wallet); - // 2. register & enroll application user with CA, which is used as client identify to make chaincode calls, stores app user identity in local wallet - await registerUser.RegisterAppUser(); + // in a real application this would be done only when a new user was required to be added + // and would be part of an administrative flow + await registerUser(caClient, wallet, userId, 'org1.department1'); - // Check to see if app user exist in wallet. - const identity = await wallet.get(registerUser.ApplicationUserId); - if (!identity) { - console.log(`An identity for the user does not exist in the wallet: ${registerUser.ApplicationUserId}`); - return; - } + // Create a new gateway instance for interacting with the fabric network. + // In a real application this would be done as the backend server session is setup for + // a user that has been verified. + const gateway = new Gateway(); - //3. Prepare to call chaincode using fabric javascript node sdk - // Create a new gateway for connecting to our peer node. - const gateway = new Gateway(); - await gateway.connect(ccp, { - wallet, - identity: registerUser.ApplicationUserId, - discovery: {enabled: true, asLocalhost: true} - }); - try { - // Get the network (channel) our contract is deployed to. - const network = await gateway.getNetwork(myChannel); + try { + // setup the gateway instance + // The user will now be able to create connections to the fabric network and be able to + // submit transactions and query. All transactions submitted by this gateway will be + // signed by this user using the credentials stored in the wallet. + await gateway.connect(ccp, { + wallet, + identity: userId, + discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally + }); - // Get the contract from the network. - const contract = network.getContract(myChaincodeName); + // Build a network instance based on the channel where the smart contract is deployed + const network = await gateway.getNetwork(channelName); - //4. Init a set of asset data on the channel using chaincode 'InitLedger' - console.log('Submit Transaction: InitLedger creates the initial set of assets on the ledger.'); - await contract.submitTransaction('InitLedger'); + // Get the contract from the network. + const contract = network.getContract(chaincodeName); - //5. *** Some example transactions are listed below *** + // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function. + // This type of transaction would only be run once by an application the first time it was started after it + // deployed the first time. Any updates to the chaincode deployed later would likely not need to run + // an "init" type function. + console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger'); + await contract.submitTransaction('InitLedger'); + console.log('*** Result: committed'); - // GetAllAssets returns all the current assets on the ledger - let result = await contract.evaluateTransaction('GetAllAssets'); - console.log(`Evaluate Transaction: GetAllAssets, result: ${prettyJSONString(result.toString())}`); + // Let's try a query type operation (function). + // This will be sent to just one peer and the results will be shown. + console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger'); + let result = await contract.evaluateTransaction('GetAllAssets'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); - console.log('\n***********************'); - console.log('Submit Transaction: CreateAsset asset13'); - //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300 - await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); + // Now let's try to submit a transaction. + // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent + // to the orderer to be committed by each of the peer's to the channel ledger. + console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); + await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); + console.log('*** Result: committed'); - console.log('Evaluate Transaction: ReadAsset asset13'); - // ReadAsset returns an asset with given assetID - result = await contract.evaluateTransaction('ReadAsset', 'asset13'); - console.log(` result: ${prettyJSONString(result.toString())}`); + console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); + result = await contract.evaluateTransaction('ReadAsset', 'asset13'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); - console.log('\n***********************'); - console.log('Evaluate Transaction: AssetExists asset1'); - // AssetExists returns 'true' if an asset with given assetID exist - result = await contract.evaluateTransaction('AssetExists', 'asset1'); - console.log(` result: ${prettyJSONString(result.toString())}`); + console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist'); + result = await contract.evaluateTransaction('AssetExists', 'asset1'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); - console.log('Submit Transaction: UpdateAsset asset1, new AppraisedValue : 350'); - // UpdateAsset updates an existing asset with new properties. Same args as CreateAsset - await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350'); + console.log('\n--> Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350'); + await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350'); + console.log('*** Result: committed'); - console.log('Evaluate Transaction: ReadAsset asset1'); - result = await contract.evaluateTransaction('ReadAsset', 'asset1'); - console.log(` result: ${prettyJSONString(result.toString())}`); + console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); + result = await contract.evaluateTransaction('ReadAsset', 'asset1'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); - try { - console.log('\nSubmit Transaction: UpdateAsset asset70'); - //Non existing asset asset70 should throw Error - await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300'); - } catch (error) { - console.log(`Expected an error on UpdateAsset of non-existing Asset: ${error}`); - } - console.log('\n***********************'); + try { + // How about we try a transactions where the executing chaincode throws an error + // Notice how the submitTransaction will throw an error containing the error thrown by the chaincode + console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error'); + await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300'); + console.log('******** FAILED to return an error'); + } catch (error) { + console.log(`*** Successfully caught the error: \n ${error}`); + } - console.log('Submit Transaction: TransferAsset asset1 from owner Tomoko > owner Tom'); - // TransferAsset transfers an asset with given ID to new owner Tom - await contract.submitTransaction('TransferAsset', 'asset1', 'Tom'); + console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom'); + await contract.submitTransaction('TransferAsset', 'asset1', 'Tom'); + console.log('*** Result: committed'); - console.log('Evaluate Transaction: ReadAsset asset1'); - result = await contract.evaluateTransaction('ReadAsset', 'asset1'); - console.log(` result: ${prettyJSONString(result.toString())}`); - - } finally { - // Disconnect from the gateway peer when all work for this client identity is complete - gateway.disconnect(); - } - } catch (error) { - console.error(`Failed to evaluate transaction: ${error}`); - process.exit(1); - } + console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); + result = await contract.evaluateTransaction('ReadAsset', 'asset1'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + } finally { + // Disconnect from the gateway when the application is closing + // This will close all connections to the network + gateway.disconnect(); + } + } catch (error) { + console.error(`******** FAILED to run the application: ${error}`); + } } - main(); diff --git a/asset-transfer-basic/application-javascript/enrollAdmin.js b/asset-transfer-basic/application-javascript/enrollAdmin.js deleted file mode 100644 index 79a794e6..00000000 --- a/asset-transfer-basic/application-javascript/enrollAdmin.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright IBM Corp. All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const FabricCAServices = require('fabric-ca-client'); -const { Wallets } = require('fabric-network'); -const fs = require('fs'); -const path = require('path'); -const adminUserId = 'admin'; -const adminUserPasswd = 'adminpw'; -const walletPath = path.join(__dirname, 'wallet'); - -async function enrollAdminUser() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; - const caTLSCACerts = caInfo.tlsCACerts.pem; - const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); - - // Create a new wallet : Note that wallet can be resfor managing identities. - const wallet = await Wallets.newFileSystemWallet(walletPath); - - // Check to see if we've already enrolled the admin user. - const identity = await wallet.get(adminUserId); - if (identity) { - console.log('An identity for the admin user already exists in the wallet'); - return; - } - - // Enroll the admin user, and import the new identity into the wallet. - const enrollment = await ca.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd }); - const x509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put(adminUserId, x509Identity); - console.log('Successfully enrolled admin user and imported it into the wallet'); - - } catch (error) { - console.error(`Failed to enroll admin user : ${error}`); - process.exit(1); - } -} - -exports.AdminUserId = adminUserId; -exports.EnrollAdminUser = enrollAdminUser; diff --git a/asset-transfer-basic/application-javascript/package.json b/asset-transfer-basic/application-javascript/package.json index cc7d93ad..b8d3ca6e 100644 --- a/asset-transfer-basic/application-javascript/package.json +++ b/asset-transfer-basic/application-javascript/package.json @@ -6,40 +6,11 @@ "node": ">=12", "npm": ">=5" }, - "scripts": { - "lint": "eslint .", - "pretest": "npm run lint", - "test": "nyc mocha --recursive" - }, "engineStrict": true, "author": "Hyperledger", "license": "Apache-2.0", "dependencies": { "fabric-ca-client": "2.2.0", "fabric-network": "2.2.0" - }, - "devDependencies": { - "chai": "^4.2.0", - "eslint": "^5.9.0", - "mocha": "^5.2.0", - "nyc": "^14.1.1", - "sinon": "^7.1.1", - "sinon-chai": "^3.3.0" - }, - "nyc": { - "exclude": [ - "coverage/**", - "test/**" - ], - "reporter": [ - "text-summary", - "html" - ], - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 } } diff --git a/asset-transfer-basic/application-javascript/registerUser.js b/asset-transfer-basic/application-javascript/registerUser.js deleted file mode 100644 index d57e4118..00000000 --- a/asset-transfer-basic/application-javascript/registerUser.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright IBM Corp. All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -'use strict'; - -const { Wallets } = require('fabric-network'); -const FabricCAServices = require('fabric-ca-client'); -const fs = require('fs'); -const path = require('path'); -const enrollAdmin = require('./enrollAdmin'); -const caChaincodeUserRole = 'client'; -const applicationUserId = 'appUser'; -const walletPath = path.join(__dirname, 'wallet'); - -async function registerAppUser() { - try { - // load the network configuration - const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); - const fileExists = fs.existsSync(ccpPath); - if (!fileExists) { - throw new Error(`no such file or directory: ${ccpPath}`); - } - const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); - - // Create a new CA client for interacting with the CA. - const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; - const ca = new FabricCAServices(caURL); - - // Create a new file system based wallet for managing identities. ; - const wallet = await Wallets.newFileSystemWallet(walletPath); - - // Check to see if we've already enrolled the user. - const userIdentity = await wallet.get(applicationUserId); - if (userIdentity) { - console.log(`An identity for the user ${applicationUserId} already exists in the wallet`); - return; - } - - // Check to see if we've already enrolled the admin user. - const adminIdentity = await wallet.get(enrollAdmin.AdminUserId); - if (!adminIdentity) { - console.log('An identity for the admin user does not exist in the wallet'); - console.log('Run the enrollAdmin.js application before retrying'); - return; - } - - // build a user object for authenticating with the CA - const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); - const adminUser = await provider.getUserContext(adminIdentity, enrollAdmin.AdminUserId); - - // Register the user, enroll the user, and import the new identity into the wallet. - // if affiliation is specified by client, the affiliation value must be configured in CA - const secret = await ca.register({ - affiliation: 'org1.department1', - enrollmentID: applicationUserId, - role: caChaincodeUserRole - }, adminUser); - const enrollment = await ca.enroll({ - enrollmentID: applicationUserId, - enrollmentSecret: secret - }); - const x509Identity = { - credentials: { - certificate: enrollment.certificate, - privateKey: enrollment.key.toBytes(), - }, - mspId: 'Org1MSP', - type: 'X.509', - }; - await wallet.put(applicationUserId, x509Identity); - console.log(`Successfully registered and enrolled user ${applicationUserId} and imported it into the wallet`); - - } catch (error) { - console.error(`Failed to register user : ${error}`); - process.exit(1); - } -} - -exports.ApplicationUserId = applicationUserId; -exports.RegisterAppUser = registerAppUser; diff --git a/test-application/javascript/AppUtil.js b/test-application/javascript/AppUtil.js new file mode 100644 index 00000000..50640306 --- /dev/null +++ b/test-application/javascript/AppUtil.js @@ -0,0 +1,40 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +exports.buildCCP = () => { + // load the common connection configuration file + const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const contents = fs.readFileSync(ccpPath, 'utf8'); + + // build a JSON object from the file contents + const ccp = JSON.parse(contents); + + console.log(`Loaded the network configuration located at ${ccpPath}`); + return ccp; +}; + +exports.buildWallet = async (Wallets, walletPath) => { + // Create a new wallet : Note that wallet is for managing identities. + let wallet; + if (walletPath) { + wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Built a file system wallet at ${walletPath}`); + } else { + wallet = await Wallets.newInMemoryWallet(); + console.log('Built an in memory wallet'); + } + + return wallet; +}; diff --git a/test-application/javascript/CAUtil.js b/test-application/javascript/CAUtil.js new file mode 100644 index 00000000..6d2d1fb7 --- /dev/null +++ b/test-application/javascript/CAUtil.js @@ -0,0 +1,101 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const adminUserId = 'admin'; +const adminUserPasswd = 'adminpw'; + +/** + * + * @param {*} FabricCAServices + * @param {*} ccp + */ +exports.buildCAClient = (FabricCAServices, ccp) => { + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const caClient = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + console.log(`Built a CA Client named ${caInfo.caName}`); + return caClient; +}; + +exports.enrollAdmin = async (caClient, wallet) => { + try { + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get(adminUserId); + if (identity) { + console.log('An identity for the admin user already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await caClient.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put(adminUserId, x509Identity); + console.log('Successfully enrolled admin user and imported it into the wallet'); + } catch (error) { + console.error(`Failed to enroll admin user : ${error}`); + } +}; + +exports.registerUser = async (caClient, wallet, userId, affiliation) => { + try { + // Check to see if we've already enrolled the user + const userIdentity = await wallet.get(userId); + if (userIdentity) { + console.log(`An identity for the user ${userId} already exists in the wallet`); + return; + } + + // Must use an admin to register a new user + const adminIdentity = await wallet.get(adminUserId); + if (!adminIdentity) { + console.log('An identity for the admin user does not exist in the wallet'); + console.log('Enroll the admin user before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, adminUserId); + + // Register the user, enroll the user, and import the new identity into the wallet. + // if affiliation is specified by client, the affiliation value must be configured in CA + const secret = await caClient.register({ + affiliation: affiliation, + enrollmentID: userId, + role: 'client' + }, adminUser); + const enrollment = await caClient.enroll({ + enrollmentID: userId, + enrollmentSecret: secret + }); + const x509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put(userId, x509Identity); + console.log(`Successfully registered and enrolled user ${userId} and imported it into the wallet`); + } catch (error) { + console.error(`Failed to register user : ${error}`); + } +};