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