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