package main import ( //"bytes" "encoding/json" "fmt" //"strconv" //"strings" "time" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) //============================================================================================================================================================================= // Structs //============================================================================================================================================================================= type SimpleChaincode struct { } type user struct { Firstname string `json:"firstname"` Lastname string `json:"lastname"` userID string `json:"userid"` DOB string `json:"dob"` Email string `json:"email"` Mobile string `json:"mobile"` Class string `json:"class"` ObjectType string `json:"docType"` } type RawMaterial struct { RMID string `json:"rmid"` Item string `json:"item"` Creator string `json:"creator"` Current_Owner string `json:"currentowner"` ClaimTags string `json:"claimtags"` Location string `json:"location"` Date string `json:"date"` CertID string `json:"certid"` ObjectType string `json:"docType"` // add quality } type FinishedGood struct { FPID string `json:"fpid"` Name string `json:"name"` Creator string `json:"creator"` Current_Owner string `json:"currentowner"` Ingredients string `json:"ingredients"` //Previous_Owner string `json:"previousowner"` Certificates string `json:"certificates"` ClaimTags string `json:"claimtags"` Location string `json:"location"` Date string `json:"date"` CertID string `json:"certid"` ObjectType string `json:"docType"` } type PurchaseOrder struct{ PurchaseOrderID string `json:"purchaseorderid"` Customer string `json:"customer"` Vendor string `json:"vendor"` ProductID string `json:"productid"` Price string `json:"price"` Date string `json:"date"` // Status string `json:"status"` ObjectType string `json:"docType"` } type Certificate struct { CertID string `json:"certid"` OrgName string `json:"orgname"` Supplier string `json:"supplier"` Status string `json:"status"` Date_effective string `json:"dateeffective"` Certifier string `json:"certifier"` ProductList string `json:"productlist"` OpDetails string `json:"opdetails"` Location string `json:"location"` ExpiryDate string `json:"expdate"` ObjectType string `json:"docType"` } // ============================================================================================================================================================= // MAIN FUNCTIONS // ============================================================================================================================================================== func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) } } // Init initializes chaincode // =========================== func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { var err error err = stub.PutState("status", []byte("Blockchain online")) //write the variable into the chaincode state if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } // Invoke - Our entry point for Invocations // ======================================== func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() fmt.Println("invoke is running " + function) // Handle different functions if function == "Register" { //create a new user return t.Register(stub, args) } else if function == "RegisterRM" { return t.RegisterRM(stub, args) } else if function == "RegisterFP" { return t.RegisterFP(stub, args) } else if function == "makePurchaseOrder" { return t.makePurchaseOrder(stub, args) } else if function == "replyPurchaseOrder" { return t.replyPurchaseOrder(stub, args) } else if function == "transferRM" { return t.transferRM(stub, args) } else if function == "transferFP" { return t.transferFP(stub, args) } else if function == "awardCert" { return t.awardCert(stub, args) } else if function == "requestCert" { return t.requestCert(stub, args) } else if function == "read" { return t.read(stub, args) } else if function == "getHistory" { return t.getHistory(stub, args) } else if function == "modifyCert" { return t.modifyCert(stub, args) } else if function == "getHistoryFG" { return t.getHistoryFG(stub, args) } fmt.Println("invoke did not find func: " + function) //error return shim.Error("Received unknown function invocation") } //============================================================================================================================================================================ // REGISTRATION CODE BELOW //============================================================================================================================================================================= func (t *SimpleChaincode) Register(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error // Firstname string `json:"firstname"` 0 // Lastname string `json:"lastname"` 1 // userID string `json:"userid"` 2 // DOB string `json:"dob"` 3 // Email string `json:"email"` 4 // Mobile string `json:"mobile"` 5 // Class string `json:"class"` 6 if len(args) != 7 { return shim.Error("Incorrect number of arguments. Expecting 7") } // ==== Input sanitation ==== fname := args[0] lname := args[1] uid := args[2] userdob := args[3] useremail := args[4] usermobile := args[5] userclass := args[6] // ==== Check if user already exists ==== fnameAsBytes, err := stub.GetState(uid) //Change this to uid not fname if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if fnameAsBytes != nil { fmt.Println("This user already exists: " + fname) return shim.Error("This user already exists: " + fname) } // ==== Create user object and marshal to JSON ==== objectType := "user" user := &user{fname, lname, uid, userdob, useremail, usermobile, userclass, objectType} userJSONasBytes, err := json.Marshal(user) if err != nil { return shim.Error(err.Error()) } // === Save user to state === err = stub.PutState(uid, userJSONasBytes) if err != nil { return shim.Error(err.Error()) } //this should be uid based range query, needs to be tested // Index indexName := "uid~fname" uidIndexKey, err := stub.CreateCompositeKey(indexName, []string{user.userID, user.Firstname}) 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 user. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value value := []byte{0x00} stub.PutState(uidIndexKey, value) // ==== user saved and indexed. Return success ==== fmt.Println("- end init user") return shim.Success(nil) } func (t *SimpleChaincode) RegisterRM(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error // RMID string `json:"rmid"` 0 // Item string `json:"item"` 1 // Creator string `json:"creator"` 2 // Current_Owner string `json:"currentowner"` 3 // ClaimTags string `json:"claimtags"` 4 // Location string `json:"location"` 5 // Date string `json:"date"` 6 // CertID string `json:"certid"` 7 // ObjectType string `json:"docType"` 8 // ==== Input sanitation ==== rawid := args[0] item := args[1] originalcreator := args[2] cowner := args[3] claimtags := args[4] loc := args[5] dates := args[6] userclass := args[7] // ==== Check if RM already exists ==== rawidAsBytes, err := stub.GetState(rawid) if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if rawidAsBytes != nil { fmt.Println("This user already exists: " + rawid) return shim.Error("This user already exists: " + rawid) } // ==== Create RM object and marshal to JSON ==== objectType := "RawMaterial" RawMaterial := &RawMaterial{rawid, item, originalcreator, cowner, claimtags, loc, dates, userclass, objectType} RawMaterialJSONasBytes, err := json.Marshal(RawMaterial) if err != nil { return shim.Error(err.Error()) } // === Save RM to state === err = stub.PutState(rawid, RawMaterialJSONasBytes) if err != nil { return shim.Error(err.Error()) } // Index indexName := "rawid~cowner" rawidIndexKey, err := stub.CreateCompositeKey(indexName, []string{RawMaterial.RMID, RawMaterial.Current_Owner}) 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 user. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value value := []byte{0x00} stub.PutState(rawidIndexKey, value) // ==== RM saved and indexed. Return success ==== fmt.Println("- end init user") return shim.Success(nil) } func (t *SimpleChaincode) RegisterFP(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error // ObjectType string `json:"docType"` 0 // FPID string `json:"fpid"` 1 // Name string `json:"name"` 2 // Creator string `json:"creator"` 3 // Current_Owner string `json:"currentowner"` 4 // Ingredients string `json:"ingredients"` 5 ////Previous_Owner string `json:"previousowner"` 6 // Certificates string `json:"certificates"` 7 // ClaimTags string `json:"claimtags"` 8 // Location string `json:"location"` 9 // Date string `json:"date"` 10 // CertID string `json:"certid"` 11 // ==== Input sanitation ==== fpid_i := args[0] name_i := args[1] originalcreator_i := args[2] cowner_i := args[3] ingredients_i := args[4] certificates_i := args[5] claimtags_i := args[6] loc_i := args[7] dates_i := args[8] certid_i := args[9] // ==== Check if FP already exists ==== fpid_iAsBytes, err := stub.GetState(fpid_i) if err != nil { return shim.Error("Failed to get user: " + err.Error()) } else if fpid_iAsBytes != nil { fmt.Println("This user already exists: " + fpid_i) return shim.Error("This user already exists: " + fpid_i) } // ==== Create object and marshal to JSON ==== objectType := "FinishedGood" FinishedGood := &FinishedGood{fpid_i, name_i, originalcreator_i, cowner_i, ingredients_i, certificates_i, claimtags_i, loc_i, dates_i, certid_i, objectType} FinishedGoodJSONasBytes, err := json.Marshal(FinishedGood) if err != nil { return shim.Error(err.Error()) } // === Save FP to state === err = stub.PutState(fpid_i, FinishedGoodJSONasBytes) if err != nil { return shim.Error(err.Error()) } //Index indexName := "fpid_i~cowner" fpiIndexKey, err := stub.CreateCompositeKey(indexName, []string{FinishedGood.FPID, FinishedGood.Current_Owner}) 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 user. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value value := []byte{0x00} stub.PutState(fpiIndexKey, value) // ==== FP saved and indexed. Return success ==== fmt.Println("- end init user") return shim.Success(nil) } //============================================================================================================================================================================= // Purchase Orders //============================================================================================================================================================================= func (t *SimpleChaincode) makePurchaseOrder(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error // PurchaseOrderID string `json:"purchaseorderid"` 0 // Customer string `json:"customer"` 1 // Vendor string `json:"vendor"` 2 // ProductID string `json:"productid"` 3 // Price string `json:"price"` 4 // Date string `json:"date"` 5 // Status string `json:"status"` Pending // ObjectType string `json:"docType"` PurchaseOrder // ==== Input sanitation ==== purchid := args[0] cust := args[1] vend := args[2] prodid := args[3] price:= args[4] dat := args[5] //stat := "Pending" // ==== Check if order already exists ==== purchAsBytes, err := stub.GetState(purchid) if err != nil { return shim.Error("Failed to get product: " + err.Error()) } else if purchAsBytes != nil { fmt.Println("This product already exists: " + purchid) return shim.Error("This product already exists: " + purchid) } // ==== Create object and marshal to JSON ==== objectType := "PurchaseOrder" PurchaseOrder := &PurchaseOrder{purchid, cust, vend, prodid, price, dat, objectType} prodJSONasBytes, err := json.Marshal(PurchaseOrder) if err != nil { return shim.Error(err.Error()) } // === Save order to state === err = stub.PutState(purchid, prodJSONasBytes) if err != nil { return shim.Error(err.Error()) } // ==== order saved and NOT indexed. Return success ==== fmt.Println("- end init user") return shim.Success(nil) } func (t *SimpleChaincode) replyPurchaseOrder(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error var key, value string var a = time.Now() var b = a.Format("20060102150405") key = args[0] var body = args[2] //this will be the yes or no value = args[1] + "-" + b +"-"+ key + " " + body err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } //=========================================================================================================================================================================== // Transferring //=========================================================================================================================================================================== func (t *SimpleChaincode) transferRM(stub shim.ChaincodeStubInterface, args []string) pb.Response { // 0 1 // "name", "bob" if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } prodid := args[0] newOwner := args[1] newLoc := args[2] newDate := args[3] assetAsBytes, err := stub.GetState(prodid) if err != nil { return shim.Error("Failed to get asset:" + err.Error()) } else if assetAsBytes == nil { return shim.Error("Assest does not exist") } assetToTransfer := RawMaterial{} err = json.Unmarshal(assetAsBytes, &assetToTransfer) //unmarshal it aka JSON.parse() if err != nil { return shim.Error(err.Error()) } assetToTransfer.Current_Owner = newOwner //change the owner assetToTransfer.Location = newLoc assetToTransfer.Date = newDate assetJSONasBytes, _ := json.Marshal(assetToTransfer) err = stub.PutState(prodid, assetJSONasBytes) //rewrite the asset if err != nil { return shim.Error(err.Error()) } fmt.Println("- end transferRM (success)") return shim.Success(nil) } func (t *SimpleChaincode) transferFP(stub shim.ChaincodeStubInterface, args []string) pb.Response { // 0 1 // "name", "bob" if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } prodid := args[0] newOwner := args[1] newLoc := args[2] newDate := args[3] assetAsBytes, err := stub.GetState(prodid) if err != nil { return shim.Error("Failed to get asset:" + err.Error()) } else if assetAsBytes == nil { return shim.Error("Assest does not exist") } assetToTransfer := FinishedGood{} err = json.Unmarshal(assetAsBytes, &assetToTransfer) //unmarshal it aka JSON.parse() if err != nil { return shim.Error(err.Error()) } assetToTransfer.Current_Owner = newOwner //change the owner assetToTransfer.Location = newLoc assetToTransfer.Date = newDate assetJSONasBytes, _ := json.Marshal(assetToTransfer) err = stub.PutState(prodid, assetJSONasBytes) //rewrite the asset if err != nil { return shim.Error(err.Error()) } fmt.Println("- end transferRM (success)") return shim.Success(nil) } //=========================================================================================================================================================================== // Certifying //=========================================================================================================================================================================== func (t *SimpleChaincode) awardCert(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error // CertID string `json:"certid"` 0 // OrgName string `json:"orgname"` 1 // Supplier string `json:"supplier"` 2 // Status string `json:"status"` 3 // Date_effective string `json:"dateeffective"` 4 // Certifier string `json:"certifier"` 5 // ProductList string `json:"productlist"` 6 // OpDetails string `json:"opdetails"` 7 // Location string `json:"location"` 8 // ExpiryDate string `json:"expdate"` 9 // ObjectType string `json:"docType"` 10 // ==== Input sanitation ==== certid := args[0] oname := args[1] supplier := args[2] stat := args[3] dateeff := args[4] certifierorg := args[5] prodlist := args[6] opdet := args[7] loc := args[8] expdat := args[9] // ==== Check if cert already exists ==== awardAsBytes, err := stub.GetState(certid) if err != nil { return shim.Error("Failed to get cert: " + err.Error()) } else if awardAsBytes != nil { fmt.Println("This cert already exists: " + certid) return shim.Error("This cert already exists: " + certid) } // ==== Create object and marshal to JSON ==== objectType := "Certificate" Certificate := &Certificate{certid, oname, supplier, stat, dateeff, certifierorg, prodlist, opdet, loc, expdat, objectType} CertJSONasBytes, err := json.Marshal(Certificate) if err != nil { return shim.Error(err.Error()) } // === Save certificate to state === err = stub.PutState(certid, CertJSONasBytes) if err != nil { return shim.Error(err.Error()) } // ==== certificate saved and NOT indexed. Return success ==== fmt.Println("- end init cert") return shim.Success(nil) } func (t *SimpleChaincode) requestCert(stub shim.ChaincodeStubInterface, args []string) pb.Response { var err error var key, value string //arg 0 is the certificate request id which will be UUID generated but for now just input //arg 1 is the suppliers id who is requesting the certificate //arg 2 is the name of the certificate //arg 3 is the products //arg 4 is the location var a = time.Now() var b = a.Format("20060102150405") key = args[0] var suppid = args[1] var name = args[2] var product = args[3] var location = args[4] value = key + "-" + b + " " + suppid + " " + name + " " + product + " " + location err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } // // This function should be used to revoke certificates but because of its open input nature can be more flexible and be used for whatever really // func (t *SimpleChaincode) modifyCert(stub shim.ChaincodeStubInterface, args []string) pb.Response { // 0 1 // "certid", "new status" if len(args) < 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } certid := args[0] // assigning first input to certid newStatus := args[1] // assigning second input to the new status of the certificate certAsBytes, err := stub.GetState(certid) //retrieving the certificate from the blockchain using the getstate function if err != nil { return shim.Error("Failed to get certificate:" + err.Error()) } else if certAsBytes == nil { return shim.Error("Certificate does not exist") } certToModify := Certificate{} err = json.Unmarshal(certAsBytes, &certToModify) //unmarshal it aka JSON.parse() if err != nil { return shim.Error(err.Error()) } certToModify.Status = newStatus //change the status certJSONasBytes, _ := json.Marshal(certToModify) err = stub.PutState(certid, certJSONasBytes) //rewrite the asset if err != nil { return shim.Error(err.Error()) } fmt.Println("- end revokeCert (success)") return shim.Success(nil) } //=========================================================================================================================================================================== // Reading //=========================================================================================================================================================================== func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A string // Entities var err error A = args[0] // Get the state from the ledger Avalbytes, err := stub.GetState(A) if err != nil { jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" return shim.Error(jsonResp) } //jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" //fmt.Printf("Query Response:%s\n", jsonResp) return shim.Success(Avalbytes) } /* func (t *SimpleChaincode) queryRMByUID(stub shim.ChaincodeStubInterface, args []string) pb.Response { // 0 // "bob" if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } owner := args[0] queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"RawMaterial\",\"owner\":\"%s\"}}", owner) queryResults, err := getQueryResultForQueryString(stub, queryString) if err != nil { return shim.Error(err.Error()) } return shim.Success(queryResults) } func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) { fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString) resultsIterator, err := stub.GetQueryResult(queryString) if err != nil { return nil, err } defer resultsIterator.Close() // buffer is a JSON array containing QueryRecords var buffer bytes.Buffer buffer.WriteString("[") bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } // Add a comma before array members, suppress it for the first array member if bArrayMemberAlreadyWritten == true { buffer.WriteString(",") } buffer.WriteString("{\"Key\":") buffer.WriteString("\"") buffer.WriteString(queryResponse.Key) buffer.WriteString("\"") buffer.WriteString(", \"Record\":") // Record is a JSON object, so we write as-is buffer.WriteString(string(queryResponse.Value)) buffer.WriteString("}") bArrayMemberAlreadyWritten = true } buffer.WriteString("]") fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String()) return buffer.Bytes(), nil } */ func (t *SimpleChaincode) getHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response { type AuditHistory struct { TxId string `json:"txId"` Value RawMaterial `json:"value"` } var history []AuditHistory; var rawmaterial RawMaterial if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } RMId := args[0] fmt.Printf("- start getHistoryForMarble: %s\n", RMId) // Get History resultsIterator, err := stub.GetHistoryForKey(RMId) if err != nil { return shim.Error(err.Error()) } defer resultsIterator.Close() for resultsIterator.HasNext() { historyData, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } var tx AuditHistory tx.TxId = historyData.TxId //copy transaction id over json.Unmarshal(historyData.Value, &rawmaterial) //un stringify it aka JSON.parse() if historyData.Value == nil { //marble has been deleted var emptyRM RawMaterial tx.Value = emptyRM //copy nil marble } else { json.Unmarshal(historyData.Value, &rawmaterial) //un stringify it aka JSON.parse() tx.Value = rawmaterial //copy marble over } history = append(history, tx) //add this tx to the list } fmt.Printf("- getHistoryForMarble returning:\n%s", history) //change to array of bytes historyAsBytes, _ := json.Marshal(history) //convert to array of bytes return shim.Success(historyAsBytes) } func (t *SimpleChaincode) getHistoryFG(stub shim.ChaincodeStubInterface, args []string) pb.Response { type AuditHistory struct { TxId string `json:"txId"` Value FinishedGood `json:"value"` } var history []AuditHistory; var finishedgood FinishedGood if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } FGId := args[0] fmt.Printf("- start getHistoryForMarble: %s\n", FGId) // Get History resultsIterator, err := stub.GetHistoryForKey(FGId) if err != nil { return shim.Error(err.Error()) } defer resultsIterator.Close() for resultsIterator.HasNext() { historyData, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } var tx AuditHistory tx.TxId = historyData.TxId //copy transaction id over json.Unmarshal(historyData.Value, &finishedgood) //un stringify it aka JSON.parse() if historyData.Value == nil { //marble has been deleted var emptyFG FinishedGood tx.Value = emptyFG //copy nil marble } else { json.Unmarshal(historyData.Value, &finishedgood) //un stringify it aka JSON.parse() tx.Value = finishedgood //copy marble over } history = append(history, tx) //add this tx to the list } fmt.Printf("- getHistoryForMarble returning:\n%s", history) //change to array of bytes historyAsBytes, _ := json.Marshal(history) //convert to array of bytes return shim.Success(historyAsBytes) }