github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/examples/chaincode/go/marbles02/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":["getHistoryForMarble","marble1"]}' 33 34 // Rich Query (Only supported if CouchDB is used as state database): 35 // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}' 36 // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}' 37 38 //The following examples demonstrate creating indexes on CouchDB 39 //Example hostname:port configurations 40 // 41 //Docker or vagrant environments: 42 // http://couchdb:5984/ 43 // 44 //Inside couchdb docker container 45 // http://127.0.0.1:5984/ 46 47 // Index for chaincodeid, docType, owner. 48 // Note that docType and owner fields must be prefixed with the "data" wrapper 49 // chaincodeid must be added for all queries 50 // 51 // Definition for use with Fauxton interface 52 // {"index":{"fields":["chaincodeid","data.docType","data.owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} 53 // 54 // example curl definition for use with command line 55 // 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 56 // 57 58 // Index for chaincodeid, docType, owner, size (descending order). 59 // Note that docType, owner and size fields must be prefixed with the "data" wrapper 60 // chaincodeid must be added for all queries 61 // 62 // Definition for use with Fauxton interface 63 // {"index":{"fields":[{"data.size":"desc"},{"chaincodeid":"desc"},{"data.docType":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortDesc","type":"json"} 64 // 65 // example curl definition for use with command line 66 // 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 67 68 // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): 69 // peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}' 70 71 // Rich Query with index design doc specified only (Only supported if CouchDB is used as state database): 72 // 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\"}"]}' 73 74 package main 75 76 import ( 77 "bytes" 78 "encoding/json" 79 "fmt" 80 "strconv" 81 "strings" 82 83 "github.com/hyperledger/fabric/core/chaincode/shim" 84 pb "github.com/hyperledger/fabric/protos/peer" 85 ) 86 87 // SimpleChaincode example simple Chaincode implementation 88 type SimpleChaincode struct { 89 } 90 91 type marble struct { 92 ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database 93 Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around 94 Color string `json:"color"` 95 Size int `json:"size"` 96 Owner string `json:"owner"` 97 } 98 99 // =================================================================================== 100 // Main 101 // =================================================================================== 102 func main() { 103 err := shim.Start(new(SimpleChaincode)) 104 if err != nil { 105 fmt.Printf("Error starting Simple chaincode: %s", err) 106 } 107 } 108 109 // Init initializes chaincode 110 // =========================== 111 func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 112 return shim.Success(nil) 113 } 114 115 // Invoke - Our entry point for Invocations 116 // ======================================== 117 func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 118 function, args := stub.GetFunctionAndParameters() 119 fmt.Println("invoke is running " + function) 120 121 // Handle different functions 122 if function == "initMarble" { //create a new marble 123 return t.initMarble(stub, args) 124 } else if function == "transferMarble" { //change owner of a specific marble 125 return t.transferMarble(stub, args) 126 } else if function == "transferMarblesBasedOnColor" { //transfer all marbles of a certain color 127 return t.transferMarblesBasedOnColor(stub, args) 128 } else if function == "delete" { //delete a marble 129 return t.delete(stub, args) 130 } else if function == "readMarble" { //read a marble 131 return t.readMarble(stub, args) 132 } else if function == "queryMarblesByOwner" { //find marbles for owner X using rich query 133 return t.queryMarblesByOwner(stub, args) 134 } else if function == "queryMarbles" { //find marbles based on an ad hoc rich query 135 return t.queryMarbles(stub, args) 136 } else if function == "getHistoryForMarble" { //get history of values for a marble 137 return t.getHistoryForMarble(stub, args) 138 } 139 140 fmt.Println("invoke did not find func: " + function) //error 141 return shim.Error("Received unknown function invocation") 142 } 143 144 // ============================================================ 145 // initMarble - create a new marble, store into chaincode state 146 // ============================================================ 147 func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response { 148 var err error 149 150 // 0 1 2 3 151 // "asdf", "blue", "35", "bob" 152 if len(args) != 4 { 153 return shim.Error("Incorrect number of arguments. Expecting 4") 154 } 155 156 // ==== Input sanitation ==== 157 fmt.Println("- start init marble") 158 if len(args[0]) <= 0 { 159 return shim.Error("1st argument must be a non-empty string") 160 } 161 if len(args[1]) <= 0 { 162 return shim.Error("2nd argument must be a non-empty string") 163 } 164 if len(args[2]) <= 0 { 165 return shim.Error("3rd argument must be a non-empty string") 166 } 167 if len(args[3]) <= 0 { 168 return shim.Error("4th argument must be a non-empty string") 169 } 170 marbleName := args[0] 171 color := strings.ToLower(args[1]) 172 owner := strings.ToLower(args[3]) 173 size, err := strconv.Atoi(args[2]) 174 if err != nil { 175 return shim.Error("3rd argument must be a numeric string") 176 } 177 178 // ==== Check if marble already exists ==== 179 marbleAsBytes, err := stub.GetState(marbleName) 180 if err != nil { 181 return shim.Error("Failed to get marble: " + err.Error()) 182 } else if marbleAsBytes != nil { 183 fmt.Println("This marble already exists: " + marbleName) 184 return shim.Error("This marble already exists: " + marbleName) 185 } 186 187 // ==== Create marble object and marshal to JSON ==== 188 objectType := "marble" 189 marble := &marble{objectType, marbleName, color, size, owner} 190 marbleJSONasBytes, err := json.Marshal(marble) 191 if err != nil { 192 return shim.Error(err.Error()) 193 } 194 //Alternatively, build the marble json string manually if you don't want to use struct marshalling 195 //marbleJSONasString := `{"docType":"Marble", "name": "` + marbleName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}` 196 //marbleJSONasBytes := []byte(str) 197 198 // === Save marble to state === 199 err = stub.PutState(marbleName, marbleJSONasBytes) 200 if err != nil { 201 return shim.Error(err.Error()) 202 } 203 204 // ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ==== 205 // An 'index' is a normal key/value entry in state. 206 // The key is a composite key, with the elements that you want to range query on listed first. 207 // In our case, the composite key is based on indexName~color~name. 208 // This will enable very efficient state range queries based on composite keys matching indexName~color~* 209 indexName := "color~name" 210 colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name}) 211 if err != nil { 212 return shim.Error(err.Error()) 213 } 214 // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble. 215 // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value 216 value := []byte{0x00} 217 stub.PutState(colorNameIndexKey, value) 218 219 // ==== Marble saved and indexed. Return success ==== 220 fmt.Println("- end init marble") 221 return shim.Success(nil) 222 } 223 224 // =============================================== 225 // readMarble - read a marble from chaincode state 226 // =============================================== 227 func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response { 228 var name, jsonResp string 229 var err error 230 231 if len(args) != 1 { 232 return shim.Error("Incorrect number of arguments. Expecting name of the marble to query") 233 } 234 235 name = args[0] 236 valAsbytes, err := stub.GetState(name) //get the marble from chaincode state 237 if err != nil { 238 jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}" 239 return shim.Error(jsonResp) 240 } else if valAsbytes == nil { 241 jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}" 242 return shim.Error(jsonResp) 243 } 244 245 return shim.Success(valAsbytes) 246 } 247 248 // ================================================== 249 // delete - remove a marble key/value pair from state 250 // ================================================== 251 func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { 252 var jsonResp string 253 var marbleJSON marble 254 if len(args) != 1 { 255 return shim.Error("Incorrect number of arguments. Expecting 1") 256 } 257 marbleName := args[0] 258 259 // to maintain the color~name index, we need to read the marble first and get its color 260 valAsbytes, err := stub.GetState(marbleName) //get the marble from chaincode state 261 if err != nil { 262 jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}" 263 return shim.Error(jsonResp) 264 } else if valAsbytes == nil { 265 jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}" 266 return shim.Error(jsonResp) 267 } 268 269 err = json.Unmarshal([]byte(valAsbytes), &marbleJSON) 270 if err != nil { 271 jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}" 272 return shim.Error(jsonResp) 273 } 274 275 err = stub.DelState(marbleName) //remove the marble from chaincode state 276 if err != nil { 277 return shim.Error("Failed to delete state:" + err.Error()) 278 } 279 280 // maintain the index 281 indexName := "color~name" 282 colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name}) 283 if err != nil { 284 return shim.Error(err.Error()) 285 } 286 287 // Delete index entry to state. 288 err = stub.DelState(colorNameIndexKey) 289 if err != nil { 290 return shim.Error("Failed to delete state:" + err.Error()) 291 } 292 return shim.Success(nil) 293 } 294 295 // =========================================================== 296 // transfer a marble by setting a new owner name on the marble 297 // =========================================================== 298 func (t *SimpleChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response { 299 300 // 0 1 301 // "name", "bob" 302 if len(args) < 2 { 303 return shim.Error("Incorrect number of arguments. Expecting 2") 304 } 305 306 marbleName := args[0] 307 newOwner := strings.ToLower(args[1]) 308 fmt.Println("- start transferMarble ", marbleName, newOwner) 309 310 marbleAsBytes, err := stub.GetState(marbleName) 311 if err != nil { 312 return shim.Error("Failed to get marble:" + err.Error()) 313 } else if marbleAsBytes == nil { 314 return shim.Error("Marble does not exist") 315 } 316 317 marbleToTransfer := marble{} 318 err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) //unmarshal it aka JSON.parse() 319 if err != nil { 320 return shim.Error(err.Error()) 321 } 322 marbleToTransfer.Owner = newOwner //change the owner 323 324 marbleJSONasBytes, _ := json.Marshal(marbleToTransfer) 325 err = stub.PutState(marbleName, marbleJSONasBytes) //rewrite the marble 326 if err != nil { 327 return shim.Error(err.Error()) 328 } 329 330 fmt.Println("- end transferMarble (success)") 331 return shim.Success(nil) 332 } 333 334 // ==== Example: GetStateByPartialCompositeKey/RangeQuery ========================================= 335 // transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner. 336 // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'. 337 // Committing peers will re-execute range queries to guarantee that result sets are stable 338 // between endorsement time and commit time. The transaction is invalidated by the 339 // committing peers if the result set has changed between endorsement time and commit time. 340 // Therefore, range queries are a safe option for performing update transactions based on query results. 341 // =========================================================================================== 342 func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response { 343 344 // 0 1 345 // "color", "bob" 346 if len(args) < 2 { 347 return shim.Error("Incorrect number of arguments. Expecting 2") 348 } 349 350 color := args[0] 351 newOwner := strings.ToLower(args[1]) 352 fmt.Println("- start transferMarblesBasedOnColor ", color, newOwner) 353 354 // Query the color~name index by color 355 // This will execute a key range query on all keys starting with 'color' 356 coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color}) 357 if err != nil { 358 return shim.Error(err.Error()) 359 } 360 defer coloredMarbleResultsIterator.Close() 361 362 // Iterate through result set and for each marble found, transfer to newOwner 363 var i int 364 for i = 0; coloredMarbleResultsIterator.HasNext(); i++ { 365 // Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key 366 colorNameKey, _, err := coloredMarbleResultsIterator.Next() 367 if err != nil { 368 return shim.Error(err.Error()) 369 } 370 371 // get the color and name from color~name composite key 372 objectType, compositeKeyParts, err := stub.SplitCompositeKey(colorNameKey) 373 if err != nil { 374 return shim.Error(err.Error()) 375 } 376 returnedColor := compositeKeyParts[0] 377 returnedMarbleName := compositeKeyParts[1] 378 fmt.Printf("- found a marble from index:%s color:%s name:%s\n", objectType, returnedColor, returnedMarbleName) 379 380 // Now call the transfer function for the found marble. 381 // Re-use the same function that is used to transfer individual marbles 382 response := t.transferMarble(stub, []string{returnedMarbleName, newOwner}) 383 // if the transfer failed break out of loop and return error 384 if response.Status != shim.OK { 385 return shim.Error("Transfer failed: " + response.Message) 386 } 387 } 388 389 responsePayload := fmt.Sprintf("Transferred %d %s marbles to %s", i, color, newOwner) 390 fmt.Println("- end transferMarblesBasedOnColor: " + responsePayload) 391 return shim.Success([]byte(responsePayload)) 392 } 393 394 // =======Rich queries ========================================================================= 395 // Two examples of rich queries are provided below (parameterized query and ad hoc query). 396 // Rich queries pass a query string to the state database. 397 // Rich queries are only supported by state database implementations 398 // that support rich query (e.g. CouchDB). 399 // The query string is in the syntax of the underlying state database. 400 // With rich queries there is no guarantee that the result set hasn't changed between 401 // endorsement time and commit time, aka 'phantom reads'. 402 // Therefore, rich queries should not be used in update transactions, unless the 403 // application handles the possibility of result set changes between endorsement and commit time. 404 // Rich queries can be used for point-in-time queries against a peer. 405 // ============================================================================================ 406 407 // ===== Example: Parameterized rich query ================================================= 408 // queryMarblesByOwner queries for marbles based on a passed in owner. 409 // This is an example of a parameterized query where the query logic is baked into the chaincode, 410 // and accepting a single query parameter (owner). 411 // Only available on state databases that support rich query (e.g. CouchDB) 412 // ========================================================================================= 413 func (t *SimpleChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response { 414 415 // 0 416 // "bob" 417 if len(args) < 1 { 418 return shim.Error("Incorrect number of arguments. Expecting 1") 419 } 420 421 owner := strings.ToLower(args[0]) 422 423 queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", owner) 424 425 queryResults, err := getQueryResultForQueryString(stub, queryString) 426 if err != nil { 427 return shim.Error(err.Error()) 428 } 429 return shim.Success(queryResults) 430 } 431 432 // ===== Example: Ad hoc rich query ======================================================== 433 // queryMarbles uses a query string to perform a query for marbles. 434 // Query string matching state database syntax is passed in and executed as is. 435 // Supports ad hoc queries that can be defined at runtime by the client. 436 // If this is not desired, follow the queryMarblesForOwner example for parameterized queries. 437 // Only available on state databases that support rich query (e.g. CouchDB) 438 // ========================================================================================= 439 func (t *SimpleChaincode) queryMarbles(stub shim.ChaincodeStubInterface, args []string) pb.Response { 440 441 // 0 442 // "queryString" 443 if len(args) < 1 { 444 return shim.Error("Incorrect number of arguments. Expecting 1") 445 } 446 447 queryString := args[0] 448 449 queryResults, err := getQueryResultForQueryString(stub, queryString) 450 if err != nil { 451 return shim.Error(err.Error()) 452 } 453 return shim.Success(queryResults) 454 } 455 456 // ========================================================================================= 457 // getQueryResultForQueryString executes the passed in query string. 458 // Result set is built and returned as a byte array containing the JSON results. 459 // ========================================================================================= 460 func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) { 461 462 fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString) 463 464 resultsIterator, err := stub.GetQueryResult(queryString) 465 if err != nil { 466 return nil, err 467 } 468 defer resultsIterator.Close() 469 470 // buffer is a JSON array containing QueryRecords 471 var buffer bytes.Buffer 472 buffer.WriteString("[") 473 474 bArrayMemberAlreadyWritten := false 475 for resultsIterator.HasNext() { 476 queryResultKey, queryResultRecord, err := resultsIterator.Next() 477 if err != nil { 478 return nil, err 479 } 480 // Add a comma before array members, suppress it for the first array member 481 if bArrayMemberAlreadyWritten == true { 482 buffer.WriteString(",") 483 } 484 buffer.WriteString("{\"Key\":") 485 buffer.WriteString("\"") 486 buffer.WriteString(queryResultKey) 487 buffer.WriteString("\"") 488 489 buffer.WriteString(", \"Record\":") 490 // Record is a JSON object, so we write as-is 491 buffer.WriteString(string(queryResultRecord)) 492 buffer.WriteString("}") 493 bArrayMemberAlreadyWritten = true 494 } 495 buffer.WriteString("]") 496 497 fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String()) 498 499 return buffer.Bytes(), nil 500 } 501 502 func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response { 503 504 if len(args) < 1 { 505 return shim.Error("Incorrect number of arguments. Expecting 1") 506 } 507 508 marbleName := args[0] 509 510 fmt.Printf("- start getHistoryForMarble: %s\n", marbleName) 511 512 resultsIterator, err := stub.GetHistoryForKey(marbleName) 513 if err != nil { 514 return shim.Error(err.Error()) 515 } 516 defer resultsIterator.Close() 517 518 // buffer is a JSON array containing historic values for the marble 519 var buffer bytes.Buffer 520 buffer.WriteString("[") 521 522 bArrayMemberAlreadyWritten := false 523 for resultsIterator.HasNext() { 524 txID, historicValue, err := resultsIterator.Next() 525 if err != nil { 526 return shim.Error(err.Error()) 527 } 528 // Add a comma before array members, suppress it for the first array member 529 if bArrayMemberAlreadyWritten == true { 530 buffer.WriteString(",") 531 } 532 buffer.WriteString("{\"TxId\":") 533 buffer.WriteString("\"") 534 buffer.WriteString(txID) 535 buffer.WriteString("\"") 536 537 buffer.WriteString(", \"Value\":") 538 // historicValue is a JSON marble, so we write as-is 539 buffer.WriteString(string(historicValue)) 540 buffer.WriteString("}") 541 bArrayMemberAlreadyWritten = true 542 } 543 buffer.WriteString("]") 544 545 fmt.Printf("- getHistoryForMarble returning:\n%s\n", buffer.String()) 546 547 return shim.Success(buffer.Bytes()) 548 }