mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
Add asset transfer ledger queries go chaincode sample
- Add new generic asset transfer sample based on the existing marbles02 sample - Add InitLedger() to create base assets - Add AssetExists() to check for an assets existence in the world state Signed-off-by: Tiffany Harris <tiffany.harris@ibm.com>
This commit is contained in:
parent
fc0c2f81d9
commit
2bd1599d83
4 changed files with 619 additions and 0 deletions
|
|
@ -0,0 +1 @@
|
|||
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// ====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"]}'
|
||||
|
||||
// ==== 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 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.
|
||||
//
|
||||
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"github.com/hyperledger/fabric-chaincode-go/shim"
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
)
|
||||
|
||||
// SimpleChaincode example simple Chaincode implementation
|
||||
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
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get asset: " + err.Error())
|
||||
} else if exists {
|
||||
return fmt.Errorf("This asset already exists: " + ID)
|
||||
}
|
||||
|
||||
objectType := "asset"
|
||||
asset := &asset{
|
||||
ObjectType: objectType,
|
||||
ID: ID,
|
||||
Color: color,
|
||||
Size: size,
|
||||
Owner: owner,
|
||||
AppraisedValue: appraisedValue,
|
||||
}
|
||||
assetJSON, err := json.Marshal(asset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(ID, assetJSON)
|
||||
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.
|
||||
// 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})
|
||||
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.
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if assetJSON == nil {
|
||||
return nil, fmt.Errorf("%s does not exist", ID)
|
||||
}
|
||||
|
||||
asset := new(asset)
|
||||
err = json.Unmarshal(assetJSON, asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return asset, nil
|
||||
}
|
||||
|
||||
// DeleteAsset - remove a asset key/value pair from state
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
var asset asset
|
||||
err = json.Unmarshal([]byte(assetJSON), &asset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode JSON of %s", assetID)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().DelState(assetID) //remove the asset from chaincode state
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to delete state:" + err.Error())
|
||||
}
|
||||
|
||||
// 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.
|
||||
return ctx.GetStub().DelState(colorNameIndexKey)
|
||||
}
|
||||
|
||||
// TransferAsset transfers a 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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get asset:" + err.Error())
|
||||
} else if assetAsBytes == nil {
|
||||
return fmt.Errorf("Asset does not exist")
|
||||
}
|
||||
|
||||
assetToTransfer := asset{}
|
||||
err = json.Unmarshal(assetAsBytes, &assetToTransfer)
|
||||
if err != nil {
|
||||
return fmt.Errorf(err.Error())
|
||||
}
|
||||
assetToTransfer.Owner = newOwner //change the owner
|
||||
|
||||
assetJSON, _ := json.Marshal(assetToTransfer)
|
||||
return ctx.GetStub().PutState(assetID, assetJSON)
|
||||
}
|
||||
|
||||
// constructQueryResponseFromIterator constructs a JSON array containing query results from
|
||||
// a given result iterator
|
||||
func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) ([]*QueryResult, error) {
|
||||
|
||||
resp := []*QueryResult{}
|
||||
|
||||
for resultsIterator.HasNext() {
|
||||
queryResponse, err := resultsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newRecord := new(QueryResult)
|
||||
err = json.Unmarshal(queryResponse.Value, newRecord)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = append(resp, newRecord)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (t *SimpleChaincode) GetAssetsByRange(ctx contractapi.TransactionContextInterface, startKey, endKey string) ([]*QueryResult, error) {
|
||||
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
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'.
|
||||
// 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})
|
||||
if err != nil {
|
||||
return fmt.Errorf(err.Error())
|
||||
}
|
||||
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
|
||||
responseRange, err := coloredAssetResultsIterator.Next()
|
||||
if err != nil {
|
||||
return fmt.Errorf(err.Error())
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
if len(compositeKeyParts) > 2 {
|
||||
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
|
||||
if err != nil {
|
||||
return fmt.Errorf("Transfer failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]*QueryResult, error) {
|
||||
queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"asset\",\"owner\":\"%s\"}}", owner)
|
||||
|
||||
return getQueryResultForQueryString(ctx, queryString)
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Example: Ad hoc rich query
|
||||
func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*QueryResult, error) {
|
||||
return getQueryResultForQueryString(ctx, queryString)
|
||||
}
|
||||
|
||||
// getQueryResultForQueryString executes the passed in query string.
|
||||
// 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
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
return constructQueryResponseFromIterator(resultsIterator)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Example: Pagination with Range Query
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
return constructQueryResponseFromIterator(resultsIterator)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Example: Pagination with Ad hoc Rich Query
|
||||
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) {
|
||||
|
||||
resultsIterator, _, err := ctx.GetStub().GetQueryResultWithPagination(queryString, pageSize, bookmark)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
return constructQueryResponseFromIterator(resultsIterator)
|
||||
}
|
||||
|
||||
// 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{}
|
||||
|
||||
for resultsIterator.HasNext() {
|
||||
response, err := resultsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
asset := new(asset)
|
||||
err = json.Unmarshal(response.Value, asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := QueryResult{
|
||||
TxId: response.TxId,
|
||||
Timestamp: time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)),
|
||||
Record: asset,
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to read from world state. %s", err.Error())
|
||||
}
|
||||
|
||||
return assetJSON != nil, nil
|
||||
}
|
||||
|
||||
// InitLedger creates sample 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},
|
||||
}
|
||||
|
||||
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. %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
chaincode, err := contractapi.NewChaincode(new(SimpleChaincode))
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating asset chaincode: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := chaincode.Start(); err != nil {
|
||||
fmt.Printf("Error starting asset chaincode: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
8
asset-transfer-ledger-queries/chaincode-go/go.mod
Normal file
8
asset-transfer-ledger-queries/chaincode-go/go.mod
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module github.com/hyperledger/fabric-samples/asset-transfer-ledger-queries/chaincode-go
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
)
|
||||
138
asset-transfer-ledger-queries/chaincode-go/go.sum
Normal file
138
asset-transfer-ledger-queries/chaincode-go/go.sum
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
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/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/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/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/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/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/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/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/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/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/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.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=
|
||||
Loading…
Reference in a new issue