github.com/lzy4123/fabric@v2.1.1+incompatible/integration/chaincode/marbles/marbles_chaincode.go (about)

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