github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/examples/marbles/marbles_chaincode.go (about)

     1  /*
     2  Licensed to the Apache Software Foundation (ASF) under one
     3  or more contributor license agreements.  See the NOTICE file
     4  distributed with this work for additional information
     5  regarding copyright ownership.  The ASF licenses this file
     6  to you under the Apache License, Version 2.0 (the
     7  "License"); you may not use this file except in compliance
     8  with the License.  You may obtain a copy of the License at
     9  
    10    http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing,
    13  software distributed under the License is distributed on an
    14  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15  KIND, either express or implied.  See the License for the
    16  specific language governing permissions and limitations
    17  under the License.
    18  */
    19  
    20  // ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
    21  
    22  // ==== Invoke marbles ====
    23  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
    24  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
    25  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
    26  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
    27  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
    28  // peer chaincode invoke -C myc1 -n marbles -c '{"Args":["delete","marble1"]}'
    29  
    30  // ==== Query marbles ====
    31  // peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
    32  // peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRange","marble1","marble3"]}'
    33  // peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
    34  
    35  // Rich Query (Only supported if CouchDB is used as state database):
    36  //   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
    37  //   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
    38  
    39  //The following examples demonstrate creating indexes on CouchDB
    40  //Example hostname:port configurations
    41  //
    42  //Docker or vagrant environments:
    43  // http://couchdb:5984/
    44  //
    45  //Inside couchdb docker container
    46  // http://127.0.0.1:5984/
    47  
    48  // Index for chaincodeid, docType, owner.
    49  // Note that docType and owner fields must be prefixed with the "data" wrapper
    50  // chaincodeid must be added for all queries
    51  //
    52  // Definition for use with Fauxton interface
    53  // {"index":{"fields":["chaincodeid","data.docType","data.owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
    54  //
    55  // example curl definition for use with command line
    56  // curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"chaincodeid\",\"data.docType\",\"data.owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1/_index
    57  //
    58  
    59  // Index for chaincodeid, docType, owner, size (descending order).
    60  // Note that docType, owner and size fields must be prefixed with the "data" wrapper
    61  // chaincodeid must be added for all queries
    62  //
    63  // Definition for use with Fauxton interface
    64  // {"index":{"fields":[{"data.size":"desc"},{"chaincodeid":"desc"},{"data.docType":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortDesc","type":"json"}
    65  //
    66  // example curl definition for use with command line
    67  // curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"data.size\":\"desc\"},{\"chaincodeid\":\"desc\"},{\"data.docType\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1/_index
    68  
    69  // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
    70  //   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
    71  
    72  // Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
    73  //   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\"}"]}'
    74  
    75  package main
    76  
    77  import (
    78  	"bytes"
    79  	"encoding/json"
    80  	"fmt"
    81  	"strconv"
    82  	"strings"
    83  	"time"
    84  
    85  	"github.com/inklabsfoundation/inkchain/core/chaincode/shim"
    86  	pb "github.com/inklabsfoundation/inkchain/protos/peer"
    87  )
    88  
    89  // SimpleChaincode example simple Chaincode implementation
    90  type SimpleChaincode struct {
    91  }
    92  
    93  type marble struct {
    94  	ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
    95  	Name       string `json:"name"`    //the fieldtags are needed to keep case from bouncing around
    96  	Color      string `json:"color"`
    97  	Size       int    `json:"size"`
    98  	Owner      string `json:"owner"`
    99  }
   100  
   101  // ===================================================================================
   102  // Main
   103  // ===================================================================================
   104  func main() {
   105  	err := shim.Start(new(SimpleChaincode))
   106  	if err != nil {
   107  		fmt.Printf("Error starting Simple chaincode: %s", err)
   108  	}
   109  }
   110  
   111  // Init initializes chaincode
   112  // ===========================
   113  func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
   114  	return shim.Success(nil)
   115  }
   116  
   117  // Invoke - Our entry point for Invocations
   118  // ========================================
   119  func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   120  	function, args := stub.GetFunctionAndParameters()
   121  	fmt.Println("invoke is running " + function)
   122  
   123  	// Handle different functions
   124  	if function == "initMarble" { //create a new marble
   125  		return t.initMarble(stub, args)
   126  	} else if function == "transferMarble" { //change owner of a specific marble
   127  		return t.transferMarble(stub, args)
   128  	} else if function == "transferMarblesBasedOnColor" { //transfer all marbles of a certain color
   129  		return t.transferMarblesBasedOnColor(stub, args)
   130  	} else if function == "delete" { //delete a marble
   131  		return t.delete(stub, args)
   132  	} else if function == "readMarble" { //read a marble
   133  		return t.readMarble(stub, args)
   134  	} else if function == "queryMarblesByOwner" { //find marbles for owner X using rich query
   135  		return t.queryMarblesByOwner(stub, args)
   136  	} else if function == "queryMarbles" { //find marbles based on an ad hoc rich query
   137  		return t.queryMarbles(stub, args)
   138  	} else if function == "getHistoryForMarble" { //get history of values for a marble
   139  		return t.getHistoryForMarble(stub, args)
   140  	} else if function == "getMarblesByRange" { //get marbles based on range query
   141  		return t.getMarblesByRange(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  	stub.PutState(colorNameIndexKey, value)
   222  
   223  	// ==== Marble saved and indexed. Return success ====
   224  	fmt.Println("- end init marble")
   225  	return shim.Success(nil)
   226  }
   227  
   228  // ===============================================
   229  // readMarble - read a marble from chaincode state
   230  // ===============================================
   231  func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   232  	var name, jsonResp string
   233  	var err error
   234  
   235  	if len(args) != 1 {
   236  		return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
   237  	}
   238  
   239  	name = args[0]
   240  	valAsbytes, err := stub.GetState(name) //get the marble from chaincode state
   241  	if err != nil {
   242  		jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
   243  		return shim.Error(jsonResp)
   244  	} else if valAsbytes == nil {
   245  		jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
   246  		return shim.Error(jsonResp)
   247  	}
   248  
   249  	return shim.Success(valAsbytes)
   250  }
   251  
   252  // ==================================================
   253  // delete - remove a marble key/value pair from state
   254  // ==================================================
   255  func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   256  	var jsonResp string
   257  	var marbleJSON marble
   258  	if len(args) != 1 {
   259  		return shim.Error("Incorrect number of arguments. Expecting 1")
   260  	}
   261  	marbleName := args[0]
   262  
   263  	// to maintain the color~name index, we need to read the marble first and get its color
   264  	valAsbytes, err := stub.GetState(marbleName) //get the marble from chaincode state
   265  	if err != nil {
   266  		jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}"
   267  		return shim.Error(jsonResp)
   268  	} else if valAsbytes == nil {
   269  		jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}"
   270  		return shim.Error(jsonResp)
   271  	}
   272  
   273  	err = json.Unmarshal([]byte(valAsbytes), &marbleJSON)
   274  	if err != nil {
   275  		jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}"
   276  		return shim.Error(jsonResp)
   277  	}
   278  
   279  	err = stub.DelState(marbleName) //remove the marble from chaincode state
   280  	if err != nil {
   281  		return shim.Error("Failed to delete state:" + err.Error())
   282  	}
   283  
   284  	// maintain the index
   285  	indexName := "color~name"
   286  	colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
   287  	if err != nil {
   288  		return shim.Error(err.Error())
   289  	}
   290  
   291  	//  Delete index entry to state.
   292  	err = stub.DelState(colorNameIndexKey)
   293  	if err != nil {
   294  		return shim.Error("Failed to delete state:" + err.Error())
   295  	}
   296  	return shim.Success(nil)
   297  }
   298  
   299  // ===========================================================
   300  // transfer a marble by setting a new owner name on the marble
   301  // ===========================================================
   302  func (t *SimpleChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   303  
   304  	//   0       1
   305  	// "name", "bob"
   306  	if len(args) < 2 {
   307  		return shim.Error("Incorrect number of arguments. Expecting 2")
   308  	}
   309  
   310  	marbleName := args[0]
   311  	newOwner := strings.ToLower(args[1])
   312  	fmt.Println("- start transferMarble ", marbleName, newOwner)
   313  
   314  	marbleAsBytes, err := stub.GetState(marbleName)
   315  	if err != nil {
   316  		return shim.Error("Failed to get marble:" + err.Error())
   317  	} else if marbleAsBytes == nil {
   318  		return shim.Error("Marble does not exist")
   319  	}
   320  
   321  	marbleToTransfer := marble{}
   322  	err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) //unmarshal it aka JSON.parse()
   323  	if err != nil {
   324  		return shim.Error(err.Error())
   325  	}
   326  	marbleToTransfer.Owner = newOwner //change the owner
   327  
   328  	marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
   329  	err = stub.PutState(marbleName, marbleJSONasBytes) //rewrite the marble
   330  	if err != nil {
   331  		return shim.Error(err.Error())
   332  	}
   333  
   334  	fmt.Println("- end transferMarble (success)")
   335  	return shim.Success(nil)
   336  }
   337  
   338  // ===========================================================================================
   339  // getMarblesByRange performs a range query based on the start and end keys provided.
   340  
   341  // Read-only function results are not typically submitted to ordering. If the read-only
   342  // results are submitted to ordering, or if the query is used in an update transaction
   343  // and submitted to ordering, then the committing peers will re-execute to guarantee that
   344  // result sets are stable between endorsement time and commit time. The transaction is
   345  // invalidated by the committing peers if the result set has changed between endorsement
   346  // time and commit time.
   347  // Therefore, range queries are a safe option for performing update transactions based on query results.
   348  // ===========================================================================================
   349  func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   350  
   351  	if len(args) < 2 {
   352  		return shim.Error("Incorrect number of arguments. Expecting 2")
   353  	}
   354  
   355  	startKey := args[0]
   356  	endKey := args[1]
   357  
   358  	resultsIterator, err := stub.GetStateByRange(startKey, endKey)
   359  	if err != nil {
   360  		return shim.Error(err.Error())
   361  	}
   362  	defer resultsIterator.Close()
   363  
   364  	// buffer is a JSON array containing QueryResults
   365  	var buffer bytes.Buffer
   366  	buffer.WriteString("[")
   367  
   368  	bArrayMemberAlreadyWritten := false
   369  	for resultsIterator.HasNext() {
   370  		queryResponse, err := resultsIterator.Next()
   371  		if err != nil {
   372  			return shim.Error(err.Error())
   373  		}
   374  		// Add a comma before array members, suppress it for the first array member
   375  		if bArrayMemberAlreadyWritten == true {
   376  			buffer.WriteString(",")
   377  		}
   378  		buffer.WriteString("{\"Key\":")
   379  		buffer.WriteString("\"")
   380  		buffer.WriteString(queryResponse.Key)
   381  		buffer.WriteString("\"")
   382  
   383  		buffer.WriteString(", \"Record\":")
   384  		// Record is a JSON object, so we write as-is
   385  		buffer.WriteString(string(queryResponse.Value))
   386  		buffer.WriteString("}")
   387  		bArrayMemberAlreadyWritten = true
   388  	}
   389  	buffer.WriteString("]")
   390  
   391  	fmt.Printf("- getMarblesByRange queryResult:\n%s\n", buffer.String())
   392  
   393  	return shim.Success(buffer.Bytes())
   394  }
   395  
   396  // ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
   397  // transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner.
   398  // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
   399  // Committing peers will re-execute range queries to guarantee that result sets are stable
   400  // between endorsement time and commit time. The transaction is invalidated by the
   401  // committing peers if the result set has changed between endorsement time and commit time.
   402  // Therefore, range queries are a safe option for performing update transactions based on query results.
   403  // ===========================================================================================
   404  func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   405  
   406  	//   0       1
   407  	// "color", "bob"
   408  	if len(args) < 2 {
   409  		return shim.Error("Incorrect number of arguments. Expecting 2")
   410  	}
   411  
   412  	color := args[0]
   413  	newOwner := strings.ToLower(args[1])
   414  	fmt.Println("- start transferMarblesBasedOnColor ", color, newOwner)
   415  
   416  	// Query the color~name index by color
   417  	// This will execute a key range query on all keys starting with 'color'
   418  	coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
   419  	if err != nil {
   420  		return shim.Error(err.Error())
   421  	}
   422  	defer coloredMarbleResultsIterator.Close()
   423  
   424  	// Iterate through result set and for each marble found, transfer to newOwner
   425  	var i int
   426  	for i = 0; coloredMarbleResultsIterator.HasNext(); i++ {
   427  		// Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key
   428  		responseRange, err := coloredMarbleResultsIterator.Next()
   429  		if err != nil {
   430  			return shim.Error(err.Error())
   431  		}
   432  
   433  		// get the color and name from color~name composite key
   434  		objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key)
   435  		if err != nil {
   436  			return shim.Error(err.Error())
   437  		}
   438  		returnedColor := compositeKeyParts[0]
   439  		returnedMarbleName := compositeKeyParts[1]
   440  		fmt.Printf("- found a marble from index:%s color:%s name:%s\n", objectType, returnedColor, returnedMarbleName)
   441  
   442  		// Now call the transfer function for the found marble.
   443  		// Re-use the same function that is used to transfer individual marbles
   444  		response := t.transferMarble(stub, []string{returnedMarbleName, newOwner})
   445  		// if the transfer failed break out of loop and return error
   446  		if response.Status != shim.OK {
   447  			return shim.Error("Transfer failed: " + response.Message)
   448  		}
   449  	}
   450  
   451  	responsePayload := fmt.Sprintf("Transferred %d %s marbles to %s", i, color, newOwner)
   452  	fmt.Println("- end transferMarblesBasedOnColor: " + responsePayload)
   453  	return shim.Success([]byte(responsePayload))
   454  }
   455  
   456  // =======Rich queries =========================================================================
   457  // Two examples of rich queries are provided below (parameterized query and ad hoc query).
   458  // Rich queries pass a query string to the state database.
   459  // Rich queries are only supported by state database implementations
   460  //  that support rich query (e.g. CouchDB).
   461  // The query string is in the syntax of the underlying state database.
   462  // With rich queries there is no guarantee that the result set hasn't changed between
   463  //  endorsement time and commit time, aka 'phantom reads'.
   464  // Therefore, rich queries should not be used in update transactions, unless the
   465  // application handles the possibility of result set changes between endorsement and commit time.
   466  // Rich queries can be used for point-in-time queries against a peer.
   467  // ============================================================================================
   468  
   469  // ===== Example: Parameterized rich query =================================================
   470  // queryMarblesByOwner queries for marbles based on a passed in owner.
   471  // This is an example of a parameterized query where the query logic is baked into the chaincode,
   472  // and accepting a single query parameter (owner).
   473  // Only available on state databases that support rich query (e.g. CouchDB)
   474  // =========================================================================================
   475  func (t *SimpleChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   476  
   477  	//   0
   478  	// "bob"
   479  	if len(args) < 1 {
   480  		return shim.Error("Incorrect number of arguments. Expecting 1")
   481  	}
   482  
   483  	owner := strings.ToLower(args[0])
   484  
   485  	queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", owner)
   486  
   487  	queryResults, err := getQueryResultForQueryString(stub, queryString)
   488  	if err != nil {
   489  		return shim.Error(err.Error())
   490  	}
   491  	return shim.Success(queryResults)
   492  }
   493  
   494  // ===== Example: Ad hoc rich query ========================================================
   495  // queryMarbles uses a query string to perform a query for marbles.
   496  // Query string matching state database syntax is passed in and executed as is.
   497  // Supports ad hoc queries that can be defined at runtime by the client.
   498  // If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
   499  // Only available on state databases that support rich query (e.g. CouchDB)
   500  // =========================================================================================
   501  func (t *SimpleChaincode) queryMarbles(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   502  
   503  	//   0
   504  	// "queryString"
   505  	if len(args) < 1 {
   506  		return shim.Error("Incorrect number of arguments. Expecting 1")
   507  	}
   508  
   509  	queryString := args[0]
   510  
   511  	queryResults, err := getQueryResultForQueryString(stub, queryString)
   512  	if err != nil {
   513  		return shim.Error(err.Error())
   514  	}
   515  	return shim.Success(queryResults)
   516  }
   517  
   518  // =========================================================================================
   519  // getQueryResultForQueryString executes the passed in query string.
   520  // Result set is built and returned as a byte array containing the JSON results.
   521  // =========================================================================================
   522  func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
   523  
   524  	fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
   525  
   526  	resultsIterator, err := stub.GetQueryResult(queryString)
   527  	if err != nil {
   528  		return nil, err
   529  	}
   530  	defer resultsIterator.Close()
   531  
   532  	// buffer is a JSON array containing QueryRecords
   533  	var buffer bytes.Buffer
   534  	buffer.WriteString("[")
   535  
   536  	bArrayMemberAlreadyWritten := false
   537  	for resultsIterator.HasNext() {
   538  		queryResponse, err := resultsIterator.Next()
   539  		if err != nil {
   540  			return nil, err
   541  		}
   542  		// Add a comma before array members, suppress it for the first array member
   543  		if bArrayMemberAlreadyWritten == true {
   544  			buffer.WriteString(",")
   545  		}
   546  		buffer.WriteString("{\"Key\":")
   547  		buffer.WriteString("\"")
   548  		buffer.WriteString(queryResponse.Key)
   549  		buffer.WriteString("\"")
   550  
   551  		buffer.WriteString(", \"Record\":")
   552  		// Record is a JSON object, so we write as-is
   553  		buffer.WriteString(string(queryResponse.Value))
   554  		buffer.WriteString("}")
   555  		bArrayMemberAlreadyWritten = true
   556  	}
   557  	buffer.WriteString("]")
   558  
   559  	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
   560  
   561  	return buffer.Bytes(), nil
   562  }
   563  
   564  func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   565  
   566  	if len(args) < 1 {
   567  		return shim.Error("Incorrect number of arguments. Expecting 1")
   568  	}
   569  
   570  	marbleName := args[0]
   571  
   572  	fmt.Printf("- start getHistoryForMarble: %s\n", marbleName)
   573  
   574  	resultsIterator, err := stub.GetHistoryForKey(marbleName)
   575  	if err != nil {
   576  		return shim.Error(err.Error())
   577  	}
   578  	defer resultsIterator.Close()
   579  
   580  	// buffer is a JSON array containing historic values for the marble
   581  	var buffer bytes.Buffer
   582  	buffer.WriteString("[")
   583  
   584  	bArrayMemberAlreadyWritten := false
   585  	for resultsIterator.HasNext() {
   586  		response, err := resultsIterator.Next()
   587  		if err != nil {
   588  			return shim.Error(err.Error())
   589  		}
   590  		// Add a comma before array members, suppress it for the first array member
   591  		if bArrayMemberAlreadyWritten == true {
   592  			buffer.WriteString(",")
   593  		}
   594  		buffer.WriteString("{\"TxId\":")
   595  		buffer.WriteString("\"")
   596  		buffer.WriteString(response.TxId)
   597  		buffer.WriteString("\"")
   598  
   599  		buffer.WriteString(", \"Value\":")
   600  		// if it was a delete operation on given key, then we need to set the
   601  		//corresponding value null. Else, we will write the response.Value
   602  		//as-is (as the Value itself a JSON marble)
   603  		if response.IsDelete {
   604  			buffer.WriteString("null")
   605  		} else {
   606  			buffer.WriteString(string(response.Value))
   607  		}
   608  
   609  		buffer.WriteString(", \"Timestamp\":")
   610  		buffer.WriteString("\"")
   611  		buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String())
   612  		buffer.WriteString("\"")
   613  
   614  		buffer.WriteString(", \"IsDelete\":")
   615  		buffer.WriteString("\"")
   616  		buffer.WriteString(strconv.FormatBool(response.IsDelete))
   617  		buffer.WriteString("\"")
   618  
   619  		buffer.WriteString("}")
   620  		bArrayMemberAlreadyWritten = true
   621  	}
   622  	buffer.WriteString("]")
   623  
   624  	fmt.Printf("- getHistoryForMarble returning:\n%s\n", buffer.String())
   625  
   626  	return shim.Success(buffer.Bytes())
   627  }