package main import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "time" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) func (t *SimpleChaincode) registerUser(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error if len(args) != 5 { return shim.Error("Incorrect number of arguments. Expecting 6") } // ==== Input sanitation ==== fmt.Println("- start init user") if len(args[0]) <= 0 { return shim.Error("1st argument must be a non-empty string") } if len(args[1]) <= 0 { return shim.Error("2nd argument must be a non-empty string") } if len(args[2]) <= 0 { return shim.Error("3rd argument must be a non-empty string") } if len(args[3]) <= 0 { return shim.Error("4th argument must be a non-empty string") } if len(args[4]) <= 0 { return shim.Error("5th argument must be a non-empty string") } userId := args[0] userName := strings.ToLower(args[1]) userFirstName := strings.ToLower(args[2]) userPhone := strings.ToLower(args[3]) userAssociation := strings.ToLower(args[4]) userAuthorized := false if err != nil { return shim.Error("3rd argument must be a numeric string") } // ==== Check if user already exists ==== ownerAsBytes, err := stub.GetState(userId) if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if ownerAsBytes != nil { fmt.Println("This user already exists: " + userId) return shim.Error("This user already exists: " + userId) } // ==== Create wallet object and marshal to JSON ==== objectType := "owner" owner := &owner{objectType, userId, userName, userFirstName, userPhone, userAssociation, userAuthorized} ownerJSONasBytes, err := json.Marshal(owner) if err != nil { return shim.Error(err.Error()) } //Alternatively, build the marble json string manually if you don't want to us$ //walletJSONasString := `{"docType":"Wallet", "id": "` + "walletType + `", "s$ //walletJSONasBytes := []byte(str) // === Save user to state === err = stub.PutState(userId, ownerJSONasBytes) if err != nil { return shim.Error(err.Error()) } // ==== Index the wallet to enable type-based range queries, e.g. return all $ // An 'index' is a normal key/value entry in state. // The key is a composite key, with the elements that you want to range query$ // In our case, the composite key is based on indexName~typeWallet~id. // This will enable very efficient state range queries based on composite key$ indexName := "userAssociation~userId" ownerAssociationIdIndexKey, err := stub.CreateCompositeKey(indexName, []string{owner.UserAssociation, owner.UserId}) if err != nil { return shim.Error(err.Error()) } // Save index entry to state. Only the key name is needed, no need to store a$ // Note - passing a 'nil' value will effectively delete the key from state, t$ value := []byte{0x00} stub.PutState(ownerAssociationIdIndexKey, value) // ==== User saved and indexed. Return success ==== fmt.Println("- end init user") return shim.Success(nil) } //=============================================== // readUser - read a user from chaincode state //=============================================== func (t *SimpleChaincode) readUser(stub shim.ChaincodeStubInterface, args []string) pb.Response { var userId, jsonResp string var err error if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting name of the user to query") } userId = args[0] valAsbytes, err := stub.GetState(userId) //get the wallet from chaincode state if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + userId + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"Wallet does not exist: " + userId + "\"}" return shim.Error(jsonResp) } return shim.Success(valAsbytes) } // ================================================== // delete - remove a wallet key/value pair from state // ================================================== func (t *SimpleChaincode) deleteUser(stub shim.ChaincodeStubInterface, args []string) pb.Response { var jsonResp string var ownerJSON owner if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } userId := args[0] // to maintain the color~name index, we need to read the marble first and get its color valAsbytes, err := stub.GetState(userId) //get the marble from chaincode state if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + userId + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"User does not exist: " + userId + "\"}" return shim.Error(jsonResp) } err = json.Unmarshal([]byte(valAsbytes), &ownerJSON) if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + userId + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"User does not exist: " + userId + "\"}" return shim.Error(jsonResp) } err = json.Unmarshal([]byte(valAsbytes), &ownerJSON) if err != nil { jsonResp = "{\"Error\":\"Failed to decode JSON of: " + userId + "\"}" return shim.Error(jsonResp) } err = stub.DelState(userId) //remove the owner from chaincode state if err != nil { return shim.Error("Failed to delete state:" + err.Error()) } // maintain the index indexName := "userAssociation~userId" ownerAssociationIdIndexKey, err := stub.CreateCompositeKey(indexName, []string{ownerJSON.UserAssociation, ownerJSON.UserId}) if err != nil { return shim.Error(err.Error()) } // Delete index entry to state. err = stub.DelState(ownerAssociationIdIndexKey) if err != nil { return shim.Error("Failed to delete state:" + err.Error()) } return shim.Success(nil) } // =========================================================== // set a permission of an account by setting authorization to create a wallet // =========================================================== func (t *SimpleChaincode) setUserPermission(stub shim.ChaincodeStubInterface, args []string) pb.Response { // 0 1 // "name", "bob" if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 2") } userId := args[0] fmt.Println("- start setUserPermission ", userId) // ==== Check if user already exists ==== ownerAsBytes, err := stub.GetState(userId) if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if ownerAsBytes == nil { fmt.Println("This wallet doesn't exists: " + userId) return shim.Error("This wallet doesn't exists: " + userId) } ownerToSet := owner{} err = json.Unmarshal(ownerAsBytes, &ownerToSet) //unmarshal it aka JSON if err != nil { return shim.Error(err.Error()) } //if walletToSet.Owner ownerToSet.UserAuthorized = true //change the permission ownerJSONasBytes, _ := json.Marshal(ownerToSet) err = stub.PutState(userId, ownerJSONasBytes) //rewrite the wallet if err != nil { return shim.Error(err.Error()) } fmt.Println("- end setUserPermission (success)") return shim.Success(nil) } func (t *SimpleChaincode) initWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 4") } // ==== Input sanitation ==== fmt.Println("- start init wallet") if len(args[0]) <= 0 { return shim.Error("1st argument must be a non-empty string") } if len(args[1]) <= 0 { return shim.Error("2nd argument must be a non-empty string") } if len(args[2]) <= 0 { return shim.Error("3rd argument must be a non-empty string") } walletId := args[0] walletType := strings.ToLower(args[1]) owner := args[2] balance := 0.0 if err != nil { return shim.Error("3rd argument must be a numeric string") } // ==== Check if user already exists ==== ownerAsBytes, err := stub.GetState(owner) if err != nil { return shim.Error("Failed to get owner: " + err.Error()) } else if ownerAsBytes == nil { fmt.Println("This user doesn't exists: " + owner) return shim.Error("This user doesn't exists: " + owner) } // ==== Check if wallet already exists ==== walletAsBytes, err := stub.GetState(walletId) if err != nil { return shim.Error("Failed to get wallet: " + err.Error()) } else if walletAsBytes != nil { fmt.Println("This wallet already exists: " + walletId) return shim.Error("This wallet already exists: " + walletId) } // ==== Create wallet object and marshal to JSON ==== objectType := "wallet" wallet := &wallet{objectType, walletId, walletType, balance, owner} walletJSONasBytes, err := json.Marshal(wallet) if err != nil { return shim.Error(err.Error()) } //Alternatively, build the marble json string manually if you don't want to use struct marshalling //walletJSONasString := `{"docType":"Wallet", "id": "` + "walletType + `", "sold": `, strconv.Itoa(size) , `"owner": "` + owner + `"}` //walletJSONasBytes := []byte(str) // === Save wallet to state === err = stub.PutState(walletId, walletJSONasBytes) if err != nil { return shim.Error(err.Error()) } // ==== Index the wallet to enable type-based range queries, e.g. return all clients wallets ==== // An 'index' is a normal key/value entry in state. // The key is a composite key, with the elements that you want to range query on listed first. // In our case, the composite key is based on indexName~typeWallet~id. // This will enable very efficient state range queries based on composite keys matching indexName~type~* indexName := "typeWallet~id" walletTypeNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{wallet.WalletType, wallet.Id}) if err != nil { return shim.Error(err.Error()) } // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value value := []byte{0x00} stub.PutState(walletTypeNameIndexKey, value) // ==== Wallet saved and indexed. Return success ==== fmt.Println("- end init wallet") return shim.Success(nil) } // =============================================== // readWallet - read a wallet from chaincode state // =============================================== func (t *SimpleChaincode) readWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { var id, jsonResp string var err error if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting name of the wallet to query") } id = args[0] valAsbytes, err := stub.GetState(id) //get the wallet from chaincode state if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + id + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"Wallet does not exist: " + id + "\"}" return shim.Error(jsonResp) } return shim.Success(valAsbytes) } // ================================================== // delete - remove a wallet key/value pair from state // ================================================== func (t *SimpleChaincode) deleteWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { var jsonResp string var walletJSON wallet if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } Id := args[0] // to maintain the color~name index, we need to read the marble first and get its color valAsbytes, err := stub.GetState(Id) //get the marble from chaincode state if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + Id + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"Wallet does not exist: " + Id + "\"}" return shim.Error(jsonResp) } err = json.Unmarshal([]byte(valAsbytes), &walletJSON) if err != nil { jsonResp = "{\"Error\":\"Failed to get state for " + Id + "\"}" return shim.Error(jsonResp) } else if valAsbytes == nil { jsonResp = "{\"Error\":\"Wallet does not exist: " + Id + "\"}" return shim.Error(jsonResp) } err = json.Unmarshal([]byte(valAsbytes), &walletJSON) if err != nil { jsonResp = "{\"Error\":\"Failed to decode JSON of: " + Id + "\"}" return shim.Error(jsonResp) } err = stub.DelState(Id) //remove the wallet from chaincode state if err != nil { return shim.Error("Failed to delete state:" + err.Error()) } // maintain the index indexName := "typeWallet~id" walletTypeNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{walletJSON.WalletType, walletJSON.Id}) if err != nil { return shim.Error(err.Error()) } // Delete index entry to state. err = stub.DelState(walletTypeNameIndexKey) if err != nil { return shim.Error("Failed to delete state:" + err.Error()) } return shim.Success(nil) } // =========================================================== // transfer a wallet by setting a new owner name on the wallet // =========================================================== func (t *SimpleChaincode) transferWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } walletId := args[0] newOwner := args[1] fmt.Println("- start transferWallet ", walletId, newOwner) // ==== Check if user already exists ==== ownerAsBytes, err := stub.GetState(newOwner) if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if ownerAsBytes == nil { fmt.Println("This user doesn't exists: " + newOwner) return shim.Error("This user doesn't exists: " + newOwner) } walletAsBytes, err := stub.GetState(walletId) if err != nil { return shim.Error("Failed to get wallet:" + err.Error()) } else if walletAsBytes == nil { return shim.Error("wallet does not exist") } walletToTransfer := wallet{} err = json.Unmarshal(walletAsBytes, &walletToTransfer) //unmarshal it aka JSON if err != nil { return shim.Error(err.Error()) } walletToTransfer.Owner = newOwner //change the owner walletJSONasBytes, _ := json.Marshal(walletToTransfer) err = stub.PutState(walletId, walletJSONasBytes) //rewrite the wallet if err != nil { return shim.Error(err.Error()) } fmt.Println("- end transferWallet (success)") return shim.Success(nil) } // =========================================================== // set a sold of a wallet by setting a new owner name on the wallet // =========================================================== func (t *SimpleChaincode) setBalanceOnWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } walletId := args[0] newBalance, err := strconv.ParseFloat(args[1], 64) fmt.Println("- start setBalanceOnWallet ", walletId, newBalance) // ==== Check if wallet already exists ==== walletAsBytes, err := stub.GetState(walletId) if err != nil { return shim.Error("Failed to get wallet: " + err.Error()) } else if walletAsBytes == nil { fmt.Println("This wallet doesn't exists: " + walletId) return shim.Error("This wallet doesn't exists: " + walletId) } walletToSet := wallet{} err = json.Unmarshal(walletAsBytes, &walletToSet) //unmarshal it aka JSON if err != nil { return shim.Error(err.Error()) } //if walletToSet.Owner walletToSet.Balance = walletToSet.Balance + newBalance //change the balance walletJSONasBytes, _ := json.Marshal(walletToSet) err = stub.PutState(walletId, walletJSONasBytes) //rewrite the wallet if err != nil { return shim.Error(err.Error()) } fmt.Println("- end setBalanceOnWallet (success)") return shim.Success(nil) } // Transaction makes payment of X units from A to B func (t *SimpleChaincode) transaction(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 3") } walletId := args[0] walletTargetId := args[1] transactionValue, err := strconv.ParseFloat(args[2], 64) fmt.Println("- start transaction ", walletId, transactionValue) // ==== Check if wallet already exists ==== walletAsBytes, err := stub.GetState(walletId) if err != nil { return shim.Error("Failed to get wallet: " + err.Error()) } else if walletAsBytes == nil { fmt.Println("This wallet doesn't exists: " + walletId) return shim.Error("This wallet doesn't exists: " + walletId) } // ==== Check if wallet already exists ==== walletTargetAsBytes, err := stub.GetState(walletTargetId) if err != nil { return shim.Error("Failed to get wallet: " + err.Error()) } else if walletTargetAsBytes == nil { fmt.Println("This wallet doesn't exists: " + walletTargetId) return shim.Error("This wallet doesn't exists: " + walletTargetId) } walletBalanceToSet := wallet{} err = json.Unmarshal(walletAsBytes, &walletBalanceToSet) //unmarshal it aka JSON if err != nil { return shim.Error(err.Error()) } if walletBalanceToSet.Balance <= 0 { return shim.Error("This wallet is not allowed to make a transaction:" + walletId) } else { walletBalanceToSet.Balance = walletBalanceToSet.Balance - transactionValue //change the balance } walletJSONasBytes, _ := json.Marshal(walletBalanceToSet) err = stub.PutState(walletId, walletJSONasBytes) //rewrite the wallet if err != nil { return shim.Error(err.Error()) } walletTargetBalanceToSet := wallet{} err = json.Unmarshal(walletTargetAsBytes, &walletTargetBalanceToSet) //unmarshal it aka JSON if err != nil { return shim.Error(err.Error()) } walletTargetBalanceToSet.Balance = walletTargetBalanceToSet.Balance + transactionValue walletTargetJSONasBytes, _ := json.Marshal(walletTargetBalanceToSet) err = stub.PutState(walletTargetId, walletTargetJSONasBytes) //rewrite the wallet if err != nil { return shim.Error(err.Error()) } fmt.Println("- end setBalanceOnWallet (success)") return shim.Success(nil) } func (t *SimpleChaincode) queryWalletsByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } owner := args[0] queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"wallet\",\"owner\":\"%s\"}}", owner) queryResults, err := getQueryResultForQueryString(stub, queryString) if err != nil { return shim.Error(err.Error()) } return shim.Success(queryResults) } func (t *SimpleChaincode) queryWalletsByType(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } walletType := strings.ToLower(args[0]) queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"wallet\",\"walletType\":\"%s\"}}", walletType) queryResults, err := getQueryResultForQueryString(stub, queryString) if err != nil { return shim.Error(err.Error()) } return shim.Success(queryResults) } // ===== Example: Ad hoc rich query ======================================================== // queryMarbles uses a query string to perform a query for marbles. // Query string matching state database syntax is passed in and executed as is. // Supports ad hoc queries that can be defined at runtime by the client. // If this is not desired, follow the queryMarblesForOwner example for parameterized queries. // Only available on state databases that support rich query (e.g. CouchDB) // ========================================================================================= func (t *SimpleChaincode) queryWallets(stub shim.ChaincodeStubInterface, args []string) pb.Response { // "queryString" if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } queryString := args[0] queryResults, err := getQueryResultForQueryString(stub, queryString) if err != nil { return shim.Error(err.Error()) } return shim.Success(queryResults) } func (t *SimpleChaincode) getHistoryForWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } walletId := args[0] fmt.Printf("- start getHistoryForWallet: %s\n", walletId) resultsIterator, err := stub.GetHistoryForKey(walletId) if err != nil { return shim.Error(err.Error()) } defer resultsIterator.Close() if resultsIterator.HasNext() { modification, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } fmt.Println("Returning information related to", string(modification.Value)) } // buffer is a JSON array containing historic values for the marble var buffer bytes.Buffer buffer.WriteString("[") bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { response, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } // Add a comma before array members, suppress it for the first array member if bArrayMemberAlreadyWritten == true { buffer.WriteString(",") } buffer.WriteString("{\"TxId\":") buffer.WriteString("\"") buffer.WriteString(response.TxId) buffer.WriteString("\"") buffer.WriteString(", \"Value\":") // if it was a delete operation on given key, then we need to set the //corresponding value null. Else, we will write the response.Value //as-is (as the Value itself a JSON marble) if response.IsDelete { buffer.WriteString("null") } else { buffer.WriteString(string(response.Value)) } buffer.WriteString(", \"Timestamp\":") buffer.WriteString("\"") buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String()) buffer.WriteString("\"") buffer.WriteString(", \"IsDelete\":") buffer.WriteString("\"") buffer.WriteString(strconv.FormatBool(response.IsDelete)) buffer.WriteString("\"") buffer.WriteString("}") bArrayMemberAlreadyWritten = true } buffer.WriteString("]") fmt.Printf("- getHistoryForWallet returning:\n%s\n", buffer.String()) return shim.Success(buffer.Bytes()) } func (t *SimpleChaincode) getAllEntities(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } startKey := args[0] endKey := args[1] resultsIterator, err := stub.GetStateByRange(startKey, endKey) if err != nil { return shim.Error(err.Error()) } defer resultsIterator.Close() buffer, err := constructQueryResponseFromIterator(resultsIterator) if err != nil { return shim.Error(err.Error()) } fmt.Printf("- getAllEntities queryResult:\n%s\n", buffer.String()) return shim.Success(buffer.Bytes()) }