github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/integration/chaincode/marbles/marbles_chaincode.go (about)

     1  /*
     2   Copyright hechain. All Rights Reserved.
     3   SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  // ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
     7  
     8  // ==== Invoke marbles ====
     9  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
    10  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
    11  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
    12  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
    13  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
    14  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["delete","marble1"]}'
    15  
    16  // ==== Query marbles ====
    17  // peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
    18  // peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRange","marble1","marble3"]}'
    19  // peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
    20  
    21  // Rich Query (Only supported if CouchDB is used as state database):
    22  // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
    23  // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
    24  
    25  // Rich Query with Pagination (Only supported if CouchDB is used as state database):
    26  // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}'
    27  
    28  // INDEXES TO SUPPORT COUCHDB RICH QUERIES
    29  //
    30  // Indexes in CouchDB are required in order to make JSON queries efficient and are required for
    31  // any JSON query with a sort. As of Hechain 1.1, indexes may be packaged alongside
    32  // chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own
    33  // text file with extension *.json with the index definition formatted in JSON following the
    34  // CouchDB index JSON syntax as documented at:
    35  // http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index
    36  //
    37  // This marbles02 example chaincode demonstrates a packaged
    38  // index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json.
    39  // For deployment of chaincode to production environments, it is recommended
    40  // to define any indexes alongside chaincode so that the chaincode and supporting indexes
    41  // are deployed automatically as a unit, once the chaincode has been installed on a peer and
    42  // instantiated on a channel. See Hechain documentation for more details.
    43  //
    44  // If you have access to the your peer's CouchDB state database in a development environment,
    45  // you may want to iteratively test various indexes in support of your chaincode queries.  You
    46  // can use the CouchDB Fauxton interface or a command line curl utility to create and update
    47  // indexes. Then once you finalize an index, include the index definition alongside your
    48  // chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment
    49  // to managed environments.
    50  //
    51  // In the examples below you can find index definitions that support marbles02
    52  // chaincode queries, along with the syntax that you can use in development environments
    53  // to create the indexes in the CouchDB Fauxton interface or a curl command line utility.
    54  //
    55  
    56  //Example hostname:port configurations to access CouchDB.
    57  //
    58  //To access CouchDB docker container from within another docker container or from vagrant environments:
    59  // http://couchdb:5984/
    60  //
    61  //Inside couchdb docker container
    62  // http://127.0.0.1:5984/
    63  
    64  // Index for docType, owner.
    65  //
    66  // Example curl command line to define index in the CouchDB channel_chaincode database
    67  // curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_marbles/_index
    68  //
    69  
    70  // Index for docType, owner, size (descending order).
    71  //
    72  // Example curl command line to define index in the CouchDB channel_chaincode database
    73  // 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_marbles/_index
    74  
    75  // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
    76  //   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
    77  
    78  // Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
    79  //   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
    80  
    81  package marbles
    82  
    83  import (
    84  	"bytes"
    85  	"encoding/json"
    86  	"fmt"
    87  	"strconv"
    88  	"strings"
    89  	"time"
    90  
    91  	"github.com/hyperledger/fabric-chaincode-go/shim"
    92  	pb "github.com/hyperledger/fabric-protos-go/peer"
    93  )
    94  
    95  // SimpleChaincode example simple Chaincode implementation
    96  type SimpleChaincode struct{}
    97  
    98  type marble struct {
    99  	ObjectType string `json:"docType"` // docType is used to distinguish the various types of objects in state database
   100  	Name       string `json:"name"`    // the fieldtags are needed to keep case from bouncing around
   101  	Color      string `json:"color"`
   102  	Size       int    `json:"size"`
   103  	Owner      string `json:"owner"`
   104  }
   105  
   106  type marbleHistory struct {
   107  	TxId      string  `json:"TxId"`
   108  	Value     *marble `json:"Value"`
   109  	Timestamp string  `json:"Timestamp"`
   110  	IsDelete  string  `json:"IsDelete"`
   111  }
   112  
   113  // Init initializes chaincode
   114  // ===========================
   115  func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
   116  	return shim.Success(nil)
   117  }
   118  
   119  // Invoke - Our entry point for Invocations
   120  // ========================================
   121  func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   122  	function, args := stub.GetFunctionAndParameters()
   123  	fmt.Println("invoke is running " + function)
   124  
   125  	// Handle different functions
   126  	if function == "initMarble" { // create a new marble
   127  		return t.initMarble(stub, args)
   128  	} else if function == "transferMarble" { // change owner of a specific marble
   129  		return t.transferMarble(stub, args)
   130  	} else if function == "transferMarblesBasedOnColor" { // transfer all marbles of a certain color
   131  		return t.transferMarblesBasedOnColor(stub, args)
   132  	} else if function == "delete" { // delete a marble
   133  		return t.delete(stub, args)
   134  	} else if function == "readMarble" { // read a marble
   135  		return t.readMarble(stub, args)
   136  	} else if function == "queryMarblesByOwner" { // find marbles for owner X using rich query
   137  		return t.queryMarblesByOwner(stub, args)
   138  	} else if function == "queryMarbles" { // find marbles based on an ad hoc rich query
   139  		return t.queryMarbles(stub, args)
   140  	} else if function == "getHistoryForMarble" { // get history of values for a marble
   141  		return t.getHistoryForMarble(stub, args)
   142  	} else if function == "getMarblesByRange" { // get marbles based on range query
   143  		return t.getMarblesByRange(stub, args)
   144  	} else if function == "getMarblesByRangeWithPagination" {
   145  		return t.getMarblesByRangeWithPagination(stub, args)
   146  	} else if function == "queryMarblesWithPagination" {
   147  		return t.queryMarblesWithPagination(stub, args)
   148  	}
   149  
   150  	fmt.Println("invoke did not find func: " + function) // error
   151  	return shim.Error("Received unknown function invocation")
   152  }
   153  
   154  // ============================================================
   155  // initMarble - create a new marble, store into chaincode state
   156  // ============================================================
   157  func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   158  	var err error
   159  
   160  	//   0       1       2     3
   161  	// "asdf", "blue", "35", "bob"
   162  	if len(args) != 4 {
   163  		return shim.Error("Incorrect number of arguments. Expecting 4")
   164  	}
   165  
   166  	// ==== Input sanitation ====
   167  	fmt.Println("- start init marble")
   168  	if len(args[0]) <= 0 {
   169  		return shim.Error("1st argument must be a non-empty string")
   170  	}
   171  	if len(args[1]) <= 0 {
   172  		return shim.Error("2nd argument must be a non-empty string")
   173  	}
   174  	if len(args[2]) <= 0 {
   175  		return shim.Error("3rd argument must be a non-empty string")
   176  	}
   177  	if len(args[3]) <= 0 {
   178  		return shim.Error("4th argument must be a non-empty string")
   179  	}
   180  	marbleName := args[0]
   181  	color := strings.ToLower(args[1])
   182  	owner := strings.ToLower(args[3])
   183  	size, err := strconv.Atoi(args[2])
   184  	if err != nil {
   185  		return shim.Error("3rd argument must be a numeric string")
   186  	}
   187  
   188  	// ==== Check if marble already exists ====
   189  	marbleAsBytes, err := stub.GetState(marbleName)
   190  	if err != nil {
   191  		return shim.Error("Failed to get marble: " + err.Error())
   192  	} else if marbleAsBytes != nil {
   193  		fmt.Println("This marble already exists: " + marbleName)
   194  		return shim.Error("This marble already exists: " + marbleName)
   195  	}
   196  
   197  	// ==== Create marble object and marshal to JSON ====
   198  	objectType := "marble"
   199  	marble := &marble{objectType, marbleName, color, size, owner}
   200  	marbleJSONasBytes, err := json.Marshal(marble)
   201  	if err != nil {
   202  		return shim.Error(err.Error())
   203  	}
   204  	// Alternatively, build the marble json string manually if you don't want to use struct marshalling
   205  	// marbleJSONasString := `{"docType":"Marble",  "name": "` + marbleName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}`
   206  	// marbleJSONasBytes := []byte(str)
   207  
   208  	// === Save marble to state ===
   209  	err = stub.PutState(marbleName, marbleJSONasBytes)
   210  	if err != nil {
   211  		return shim.Error(err.Error())
   212  	}
   213  
   214  	//  ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ====
   215  	//  An 'index' is a normal key/value entry in state.
   216  	//  The key is a composite key, with the elements that you want to range query on listed first.
   217  	//  In our case, the composite key is based on indexName~color~name.
   218  	//  This will enable very efficient state range queries based on composite keys matching indexName~color~*
   219  	indexName := "color~name"
   220  	colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name})
   221  	if err != nil {
   222  		return shim.Error(err.Error())
   223  	}
   224  	//  Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
   225  	//  Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
   226  	value := []byte{0x00}
   227  	err = stub.PutState(colorNameIndexKey, value)
   228  	if err != nil {
   229  		return shim.Error(err.Error())
   230  	}
   231  
   232  	// ==== Marble saved and indexed. Return success ====
   233  	fmt.Println("- end init marble")
   234  	return shim.Success(nil)
   235  }
   236  
   237  // ===============================================
   238  // readMarble - read a marble from chaincode state
   239  // ===============================================
   240  func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   241  	var name, jsonResp string
   242  	var err error
   243  
   244  	if len(args) != 1 {
   245  		return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
   246  	}
   247  
   248  	name = args[0]
   249  	valAsbytes, err := stub.GetState(name) // get the marble from chaincode state
   250  	if err != nil {
   251  		jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
   252  		return shim.Error(jsonResp)
   253  	} else if valAsbytes == nil {
   254  		jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
   255  		return shim.Error(jsonResp)
   256  	}
   257  
   258  	return shim.Success(valAsbytes)
   259  }
   260  
   261  // ==================================================
   262  // delete - remove a marble key/value pair from state
   263  // ==================================================
   264  func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   265  	var jsonResp string
   266  	var marbleJSON marble
   267  	if len(args) != 1 {
   268  		return shim.Error("Incorrect number of arguments. Expecting 1")
   269  	}
   270  	marbleName := args[0]
   271  
   272  	// to maintain the color~name index, we need to read the marble first and get its color
   273  	valAsbytes, err := stub.GetState(marbleName) // get the marble from chaincode state
   274  	if err != nil {
   275  		jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}"
   276  		return shim.Error(jsonResp)
   277  	} else if valAsbytes == nil {
   278  		jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}"
   279  		return shim.Error(jsonResp)
   280  	}
   281  
   282  	err = json.Unmarshal([]byte(valAsbytes), &marbleJSON)
   283  	if err != nil {
   284  		jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}"
   285  		return shim.Error(jsonResp)
   286  	}
   287  
   288  	err = stub.DelState(marbleName) // remove the marble from chaincode state
   289  	if err != nil {
   290  		return shim.Error("Failed to delete state:" + err.Error())
   291  	}
   292  
   293  	// maintain the index
   294  	indexName := "color~name"
   295  	colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
   296  	if err != nil {
   297  		return shim.Error(err.Error())
   298  	}
   299  
   300  	//  Delete index entry to state.
   301  	err = stub.DelState(colorNameIndexKey)
   302  	if err != nil {
   303  		return shim.Error("Failed to delete state:" + err.Error())
   304  	}
   305  	return shim.Success(nil)
   306  }
   307  
   308  // ===========================================================
   309  // transfer a marble by setting a new owner name on the marble
   310  // ===========================================================
   311  func (t *SimpleChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   312  	//   0       1
   313  	// "name", "bob"
   314  	if len(args) < 2 {
   315  		return shim.Error("Incorrect number of arguments. Expecting 2")
   316  	}
   317  
   318  	marbleName := args[0]
   319  	newOwner := strings.ToLower(args[1])
   320  	fmt.Println("- start transferMarble ", marbleName, newOwner)
   321  
   322  	marbleAsBytes, err := stub.GetState(marbleName)
   323  	if err != nil {
   324  		return shim.Error("Failed to get marble:" + err.Error())
   325  	} else if marbleAsBytes == nil {
   326  		return shim.Error("Marble does not exist")
   327  	}
   328  
   329  	marbleToTransfer := marble{}
   330  	err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) // unmarshal it aka JSON.parse()
   331  	if err != nil {
   332  		return shim.Error(err.Error())
   333  	}
   334  	marbleToTransfer.Owner = newOwner // change the owner
   335  
   336  	marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
   337  	err = stub.PutState(marbleName, marbleJSONasBytes) // rewrite the marble
   338  	if err != nil {
   339  		return shim.Error(err.Error())
   340  	}
   341  
   342  	fmt.Println("- end transferMarble (success)")
   343  	return shim.Success(nil)
   344  }
   345  
   346  // ===========================================================================================
   347  // constructQueryResponseFromIterator constructs a JSON array containing query results from
   348  // a given result iterator
   349  // ===========================================================================================
   350  func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) (*bytes.Buffer, error) {
   351  	// buffer is a JSON array containing QueryResults
   352  	var buffer bytes.Buffer
   353  	buffer.WriteString("[")
   354  
   355  	bArrayMemberAlreadyWritten := false
   356  	for resultsIterator.HasNext() {
   357  		queryResponse, err := resultsIterator.Next()
   358  		if err != nil {
   359  			return nil, err
   360  		}
   361  		// Add a comma before array members, suppress it for the first array member
   362  		if bArrayMemberAlreadyWritten {
   363  			buffer.WriteString(",")
   364  		}
   365  		buffer.WriteString("{\"Key\":")
   366  		buffer.WriteString("\"")
   367  		buffer.WriteString(queryResponse.Key)
   368  		buffer.WriteString("\"")
   369  
   370  		buffer.WriteString(", \"Record\":")
   371  		// Record is a JSON object, so we write as-is
   372  		buffer.WriteString(string(queryResponse.Value))
   373  		buffer.WriteString("}")
   374  		bArrayMemberAlreadyWritten = true
   375  	}
   376  	buffer.WriteString("]")
   377  
   378  	return &buffer, nil
   379  }
   380  
   381  // ===========================================================================================
   382  // addPaginationMetadataToQueryResults adds QueryResponseMetadata, which contains pagination
   383  // info, to the constructed query results
   384  // ===========================================================================================
   385  func addPaginationMetadataToQueryResults(buffer *bytes.Buffer, responseMetadata *pb.QueryResponseMetadata) *bytes.Buffer {
   386  	buffer.WriteString("[{\"ResponseMetadata\":{\"RecordsCount\":")
   387  	buffer.WriteString("\"")
   388  	buffer.WriteString(fmt.Sprintf("%v", responseMetadata.FetchedRecordsCount))
   389  	buffer.WriteString("\"")
   390  	buffer.WriteString(", \"Bookmark\":")
   391  	buffer.WriteString("\"")
   392  	buffer.WriteString(responseMetadata.Bookmark)
   393  	buffer.WriteString("\"}}]")
   394  
   395  	return buffer
   396  }
   397  
   398  // ===========================================================================================
   399  // getMarblesByRange performs a range query based on the start and end keys provided.
   400  
   401  // Read-only function results are not typically submitted to ordering. If the read-only
   402  // results are submitted to ordering, or if the query is used in an update transaction
   403  // and submitted to ordering, then the committing peers will re-execute to guarantee that
   404  // result sets are stable between endorsement time and commit time. The transaction is
   405  // invalidated by the committing peers if the result set has changed between endorsement
   406  // time and commit time.
   407  // Therefore, range queries are a safe option for performing update transactions based on query results.
   408  // ===========================================================================================
   409  func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   410  	if len(args) < 2 {
   411  		return shim.Error("Incorrect number of arguments. Expecting 2")
   412  	}
   413  
   414  	startKey := args[0]
   415  	endKey := args[1]
   416  
   417  	resultsIterator, err := stub.GetStateByRange(startKey, endKey)
   418  	if err != nil {
   419  		return shim.Error(err.Error())
   420  	}
   421  	defer resultsIterator.Close()
   422  
   423  	buffer, err := constructQueryResponseFromIterator(resultsIterator)
   424  	if err != nil {
   425  		return shim.Error(err.Error())
   426  	}
   427  
   428  	fmt.Printf("- getMarblesByRange queryResult:\n%s\n", buffer.String())
   429  
   430  	return shim.Success(buffer.Bytes())
   431  }
   432  
   433  // ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
   434  // transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner.
   435  // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
   436  // Committing peers will re-execute range queries to guarantee that result sets are stable
   437  // between endorsement time and commit time. The transaction is invalidated by the
   438  // committing peers if the result set has changed between endorsement time and commit time.
   439  // Therefore, range queries are a safe option for performing update transactions based on query results.
   440  // ===========================================================================================
   441  func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   442  	//   0       1
   443  	// "color", "bob"
   444  	if len(args) < 2 {
   445  		return shim.Error("Incorrect number of arguments. Expecting 2")
   446  	}
   447  
   448  	color := args[0]
   449  	newOwner := strings.ToLower(args[1])
   450  	fmt.Println("- start transferMarblesBasedOnColor ", color, newOwner)
   451  
   452  	// Query the color~name index by color
   453  	// This will execute a key range query on all keys starting with 'color'
   454  	coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
   455  	if err != nil {
   456  		return shim.Error(err.Error())
   457  	}
   458  	defer coloredMarbleResultsIterator.Close()
   459  
   460  	// Iterate through result set and for each marble found, transfer to newOwner
   461  	var i int
   462  	for i = 0; coloredMarbleResultsIterator.HasNext(); i++ {
   463  		// Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key
   464  		responseRange, err := coloredMarbleResultsIterator.Next()
   465  		if err != nil {
   466  			return shim.Error(err.Error())
   467  		}
   468  
   469  		// get the color and name from color~name composite key
   470  		objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key)
   471  		if err != nil {
   472  			return shim.Error(err.Error())
   473  		}
   474  		returnedColor := compositeKeyParts[0]
   475  		returnedMarbleName := compositeKeyParts[1]
   476  		fmt.Printf("- found a marble from index:%s color:%s name:%s\n", objectType, returnedColor, returnedMarbleName)
   477  
   478  		// Now call the transfer function for the found marble.
   479  		// Re-use the same function that is used to transfer individual marbles
   480  		response := t.transferMarble(stub, []string{returnedMarbleName, newOwner})
   481  		// if the transfer failed break out of loop and return error
   482  		if response.Status != shim.OK {
   483  			return shim.Error("Transfer failed: " + response.Message)
   484  		}
   485  	}
   486  
   487  	responsePayload := fmt.Sprintf("Transferred %d %s marbles to %s", i, color, newOwner)
   488  	fmt.Println("- end transferMarblesBasedOnColor: " + responsePayload)
   489  	return shim.Success([]byte(responsePayload))
   490  }
   491  
   492  // =======Rich queries =========================================================================
   493  // Two examples of rich queries are provided below (parameterized query and ad hoc query).
   494  // Rich queries pass a query string to the state database.
   495  // Rich queries are only supported by state database implementations
   496  //  that support rich query (e.g. CouchDB).
   497  // The query string is in the syntax of the underlying state database.
   498  // With rich queries there is no guarantee that the result set hasn't changed between
   499  //  endorsement time and commit time, aka 'phantom reads'.
   500  // Therefore, rich queries should not be used in update transactions, unless the
   501  // application handles the possibility of result set changes between endorsement and commit time.
   502  // Rich queries can be used for point-in-time queries against a peer.
   503  // ============================================================================================
   504  
   505  // ===== Example: Parameterized rich query =================================================
   506  // queryMarblesByOwner queries for marbles based on a passed in owner.
   507  // This is an example of a parameterized query where the query logic is baked into the chaincode,
   508  // and accepting a single query parameter (owner).
   509  // Only available on state databases that support rich query (e.g. CouchDB)
   510  // =========================================================================================
   511  func (t *SimpleChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   512  	//   0
   513  	// "bob"
   514  	if len(args) < 1 {
   515  		return shim.Error("Incorrect number of arguments. Expecting 1")
   516  	}
   517  
   518  	owner := strings.ToLower(args[0])
   519  
   520  	queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", owner)
   521  
   522  	queryResults, err := getQueryResultForQueryString(stub, queryString)
   523  	if err != nil {
   524  		return shim.Error(err.Error())
   525  	}
   526  	return shim.Success(queryResults)
   527  }
   528  
   529  // ===== Example: Ad hoc rich query ========================================================
   530  // queryMarbles uses a query string to perform a query for marbles.
   531  // Query string matching state database syntax is passed in and executed as is.
   532  // Supports ad hoc queries that can be defined at runtime by the client.
   533  // If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
   534  // Only available on state databases that support rich query (e.g. CouchDB)
   535  // =========================================================================================
   536  func (t *SimpleChaincode) queryMarbles(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   537  	//   0
   538  	// "queryString"
   539  	if len(args) < 1 {
   540  		return shim.Error("Incorrect number of arguments. Expecting 1")
   541  	}
   542  
   543  	queryString := args[0]
   544  
   545  	queryResults, err := getQueryResultForQueryString(stub, queryString)
   546  	if err != nil {
   547  		return shim.Error(err.Error())
   548  	}
   549  	return shim.Success(queryResults)
   550  }
   551  
   552  // =========================================================================================
   553  // getQueryResultForQueryString executes the passed in query string.
   554  // Result set is built and returned as a byte array containing the JSON results.
   555  // =========================================================================================
   556  func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
   557  	fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
   558  
   559  	resultsIterator, err := stub.GetQueryResult(queryString)
   560  	if err != nil {
   561  		return nil, err
   562  	}
   563  	defer resultsIterator.Close()
   564  
   565  	buffer, err := constructQueryResponseFromIterator(resultsIterator)
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  
   570  	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
   571  
   572  	return buffer.Bytes(), nil
   573  }
   574  
   575  // ====== Pagination =========================================================================
   576  // Pagination provides a method to retrieve records with a defined pagesize and
   577  // start point (bookmark).  An empty string bookmark defines the first "page" of a query
   578  // result.  Paginated queries return a bookmark that can be used in
   579  // the next query to retrieve the next page of results.  Paginated queries extend
   580  // rich queries and range queries to include a pagesize and bookmark.
   581  //
   582  // Two examples are provided in this example.  The first is getMarblesByRangeWithPagination
   583  // which executes a paginated range query.
   584  // The second example is a paginated query for rich ad-hoc queries.
   585  // =========================================================================================
   586  
   587  // ====== Example: Pagination with Range Query ===============================================
   588  // getMarblesByRangeWithPagination performs a range query based on the start & end key,
   589  // page size and a bookmark.
   590  
   591  // The number of fetched records will be equal to or lesser than the page size.
   592  // Paginated range queries are only valid for read only transactions.
   593  // ===========================================================================================
   594  func (t *SimpleChaincode) getMarblesByRangeWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   595  	if len(args) < 4 {
   596  		return shim.Error("Incorrect number of arguments. Expecting 4")
   597  	}
   598  
   599  	startKey := args[0]
   600  	endKey := args[1]
   601  	// return type of ParseInt is int64
   602  	pageSize, err := strconv.ParseInt(args[2], 10, 32)
   603  	if err != nil {
   604  		return shim.Error(err.Error())
   605  	}
   606  	bookmark := args[3]
   607  
   608  	resultsIterator, responseMetadata, err := stub.GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark)
   609  	if err != nil {
   610  		return shim.Error(err.Error())
   611  	}
   612  	defer resultsIterator.Close()
   613  
   614  	buffer, err := constructQueryResponseFromIterator(resultsIterator)
   615  	if err != nil {
   616  		return shim.Error(err.Error())
   617  	}
   618  
   619  	bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)
   620  
   621  	fmt.Printf("- getMarblesByRange queryResult:\n%s\n", bufferWithPaginationInfo.String())
   622  
   623  	return shim.Success(buffer.Bytes())
   624  }
   625  
   626  // ===== Example: Pagination with Ad hoc Rich Query ========================================================
   627  // queryMarblesWithPagination uses a query string, page size and a bookmark to perform a query
   628  // for marbles. Query string matching state database syntax is passed in and executed as is.
   629  // The number of fetched records would be equal to or lesser than the specified page size.
   630  // Supports ad hoc queries that can be defined at runtime by the client.
   631  // If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
   632  // Only available on state databases that support rich query (e.g. CouchDB)
   633  // Paginated queries are only valid for read only transactions.
   634  // =========================================================================================
   635  func (t *SimpleChaincode) queryMarblesWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   636  	//   0
   637  	// "queryString"
   638  	if len(args) < 3 {
   639  		return shim.Error("Incorrect number of arguments. Expecting 3")
   640  	}
   641  
   642  	queryString := args[0]
   643  	// return type of ParseInt is int64
   644  	pageSize, err := strconv.ParseInt(args[1], 10, 32)
   645  	if err != nil {
   646  		return shim.Error(err.Error())
   647  	}
   648  	bookmark := args[2]
   649  
   650  	queryResults, err := getQueryResultForQueryStringWithPagination(stub, queryString, int32(pageSize), bookmark)
   651  	if err != nil {
   652  		return shim.Error(err.Error())
   653  	}
   654  	return shim.Success(queryResults)
   655  }
   656  
   657  // =========================================================================================
   658  // getQueryResultForQueryStringWithPagination executes the passed in query string with
   659  // pagination info. Result set is built and returned as a byte array containing the JSON results.
   660  // =========================================================================================
   661  func getQueryResultForQueryStringWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) ([]byte, error) {
   662  	fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
   663  
   664  	resultsIterator, responseMetadata, err := stub.GetQueryResultWithPagination(queryString, pageSize, bookmark)
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  	defer resultsIterator.Close()
   669  
   670  	buffer, err := constructQueryResponseFromIterator(resultsIterator)
   671  	if err != nil {
   672  		return nil, err
   673  	}
   674  
   675  	bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)
   676  
   677  	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", bufferWithPaginationInfo.String())
   678  
   679  	return buffer.Bytes(), nil
   680  }
   681  
   682  func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   683  	if len(args) < 1 {
   684  		return shim.Error("Incorrect number of arguments. Expecting 1")
   685  	}
   686  
   687  	marbleName := args[0]
   688  
   689  	fmt.Printf("- start getHistoryForMarble: %s\n", marbleName)
   690  
   691  	resultsIterator, err := stub.GetHistoryForKey(marbleName)
   692  	if err != nil {
   693  		return shim.Error(err.Error())
   694  	}
   695  	defer resultsIterator.Close()
   696  
   697  	result := []*marbleHistory{}
   698  
   699  	for resultsIterator.HasNext() {
   700  		response, err := resultsIterator.Next()
   701  		if err != nil {
   702  			return shim.Error(err.Error())
   703  		}
   704  
   705  		var value *marble = nil
   706  		if !response.IsDelete {
   707  			value = &marble{}
   708  			err = json.Unmarshal(response.Value, value)
   709  			if err != nil {
   710  				return shim.Error(err.Error())
   711  			}
   712  		}
   713  
   714  		history := &marbleHistory{
   715  			TxId:      response.TxId,
   716  			Value:     value,
   717  			Timestamp: time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String(),
   718  			IsDelete:  strconv.FormatBool(response.IsDelete),
   719  		}
   720  		result = append(result, history)
   721  	}
   722  
   723  	bytes, err := json.Marshal(result)
   724  	if err != nil {
   725  		return shim.Error(err.Error())
   726  	}
   727  
   728  	fmt.Printf("- getHistoryForMarble returning:\n%s\n", string(bytes))
   729  	return shim.Success(bytes)
   730  }