Skip to content

Instantly share code, notes, and snippets.

@risabhsharma71
Created August 27, 2018 11:28
Show Gist options
  • Save risabhsharma71/8aee628d255c3c3f28b24a1cd1882357 to your computer and use it in GitHub Desktop.
Save risabhsharma71/8aee628d255c3c3f28b24a1cd1882357 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
type ApproveReject struct {
Decision []Request `json:decision`
}
type Request struct {
RequestedFrom string `json: requestedfrom`
requestId string `json: requestid`
Id string `json:id`
Quantity string `json:quantity`
Status string `json:status`
}
type Transaction struct {
ObjectType string `json:objectType`
Id string `json:id`
JsonObj string `json:jsonObj`
Quantity string `json:quantity`
Transferrer string `json:transferrer`
Receiver string `json:receiver`
Dispatchedto []string `json: dispatchedto`
}
// ===================================================================================
// Main
// ===================================================================================
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 {
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 == "createProduct" { //create a new marble
return t.createProduct(stub, args)
} else if function == "transferMarble" { //change owner of a specific marble
return t.transferProduct(stub, args)
} else if function == "readProduct" { //read a marble
return t.readProduct(stub, args)
} else if function == "getHistoryForMarble" { //get history of values for a marble
return t.getHistoryForProduct(stub, args)
} else if function == "requestProduct" { //get history of values for a marble
return t.requestProduct(stub, args)
}
fmt.Println("invoke did not find func: " + function) //error
return shim.Error("Received unknown function invocation")
}
// ============================================================
// initMarble - create a new product, store into chaincode state
// ============================================================
func (t *SimpleChaincode) createProduct(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var err error
// 0 1 2
// "id", "jsonobj", "quantity"
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
Id := args[0]
Jsonobj := args[1]
quantity := args[2]
// ==== Check if product already exists ====
ProductAsBytes, err := stub.GetState(Id)
if err != nil {
return shim.Error("Failed to get marble: " + err.Error())
} else if ProductAsBytes != nil {
fmt.Println("This product with the product-ID " + Id + "already exists: ")
return shim.Error("This product with the product-ID " + Id + "already exists: ")
}
// ==== Create marble object and marshal to JSON ====
Product := &Transaction{ObjectType: "Transaction", Id: Id, JsonObj: Jsonobj, Quantity: quantity, Transferrer: "Manufacturer"}
ProductJSONasBytes, err := json.Marshal(Product)
if err != nil {
return shim.Error(err.Error())
}
// === Save marble to state ===
err = stub.PutState(Id, ProductJSONasBytes)
if err != nil {
return shim.Error(err.Error())
}
// ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ====
// 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~color~name.
// This will enable very efficient state range queries based on composite keys matching indexName~color~*
indexName := "transferrer~receiver"
colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{Product.Transferrer, Product.Receiver})
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(colorNameIndexKey, value)
// ==== Marble saved and indexed. Return success ====
fmt.Println("- end init marble")
return shim.Success(nil)
}
// ===============================================
// readMarble - read a marble from chaincode state
// ===============================================
func (t *SimpleChaincode) readProduct(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 marble to query")
}
id = args[0]
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\":\"Marble does not exist: " + id + "\"}"
return shim.Error(jsonResp)
}
return shim.Success(valAsbytes)
}
// ===========================================================
// transfer a marble by setting a new owner name on the marble
// ===========================================================
func (t *SimpleChaincode) transferProduct(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// 0 1 2 3 4 5
// "id", "jsonobj","Quantity" "apporved/rejected" "requestid" "decission id"
if len(args) < 5 {
return shim.Error("Incorrect number of arguments. Expecting 2")
}
id := args[0]
newOwner := strings.ToLower(args[1])
quantity := args[2]
decision := args[3]
requestid := args[4]
RequestedFrom := args[5]
if decision == "approved" {
RequestAsbytes, err := stub.GetState(RequestedFrom)
if err != nil {
return shim.Error("Failed to get requested proposal:" + err.Error())
}
requestArray := ApproveReject{}
err = json.Unmarshal(RequestAsbytes, &requestArray.Decision) //unmarshal it aka JSON.parse()
if err != nil {
return shim.Error(err.Error())
}
for i := 0; i < len(requestArray.Decision); i++ {
if requestArray.Decision[i].requestId == requestid {
requestArray.Decision[i].Status = "Approved"
break
}
}
requestasBytes, _ := json.Marshal(requestArray)
err = stub.PutState(RequestedFrom, requestasBytes) //rewrite the marble
if err != nil {
return shim.Error(err.Error())
}
fmt.Println("- changed the status of request (success)")
fmt.Println("- starting transfer of products ", id, newOwner, quantity)
productAsBytes, err := stub.GetState(id)
if err != nil {
return shim.Error("Failed to get marble:" + err.Error())
} else if productAsBytes == nil {
return shim.Error("Marble does not exist")
}
productToTransfer := Transaction{}
err = json.Unmarshal(productAsBytes, &productToTransfer) //unmarshal it aka JSON.parse()
if err != nil {
return shim.Error(err.Error())
}
//change the owner
intQuantity, err := strconv.Atoi(productToTransfer.Quantity)
requestedQuantity, err := strconv.Atoi(quantity)
newQuantity := intQuantity - requestedQuantity
stringQuantity := strconv.Itoa(newQuantity)
productToTransfer.Transferrer = newOwner
productToTransfer.Quantity = stringQuantity
productJSONasBytes, _ := json.Marshal(productToTransfer)
err = stub.PutState(id, productJSONasBytes) //rewrite the marble
if err != nil {
return shim.Error(err.Error())
}
fmt.Println("- end transferMarble (success)")
return shim.Success(nil)
} else if decision == "rejected" {
RequestAsbytes, err := stub.GetState(RequestedFrom)
if err != nil {
return shim.Error("Failed to get requested proposal:" + err.Error())
}
requestArray := ApproveReject{}
err = json.Unmarshal(RequestAsbytes, &requestArray.Decision) //unmarshal it aka JSON.parse()
if err != nil {
return shim.Error(err.Error())
}
for i := 0; i < len(requestArray.Decision); i++ {
if requestArray.Decision[i].requestId == requestid {
requestArray.Decision[i].Status = decision
break
}
}
requestasBytes, _ := json.Marshal(requestArray)
err = stub.PutState(RequestedFrom, requestasBytes) //rewrite the marble
if err != nil {
return shim.Error(err.Error())
}
fmt.Println("- changed the status of request (success)")
} else {
fmt.Println("not a valid option")
return shim.Success(nil)
}
return shim.Success(nil)
}
// ===========================================================================================
// getMarblesByRange performs a range query based on the start and end keys provided.
// Read-only function results are not typically submitted to ordering. If the read-only
// results are submitted to ordering, or if the query is used in an update transaction
// and submitted to ordering, then the committing peers will re-execute to guarantee that
// result sets are stable between endorsement time and commit time. The transaction is
// invalidated by the committing peers if the result set has changed between endorsement
// time and commit time.
// Therefore, range queries are a safe option for performing update transactions based on query results.
// ===========================================================================================
func (t *SimpleChaincode) getproductByRange(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 is a JSON array containing QueryResults
var buffer bytes.Buffer
buffer.WriteString("[")
bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
queryResponse, 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("{\"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("- getMarblesByRange queryResult:\n%s\n", buffer.String())
return shim.Success(buffer.Bytes())
}
//==============get history for a particular product===============================================//
func (t *SimpleChaincode) getHistoryForProduct(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) < 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
marbleName := args[0]
fmt.Printf("- start getHistoryForMarble: %s\n", marbleName)
resultsIterator, err := stub.GetHistoryForKey(marbleName)
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close()
// 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("- getHistoryForMarble returning:\n%s\n", buffer.String())
return shim.Success(buffer.Bytes())
}
//=================request product==============================================//
func (t *SimpleChaincode) requestProduct(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var requestFrom, id, jsonResp, quantity, requestid string
var err error
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
}
requestFrom = args[0]
id = args[1]
quantity = args[2]
requestid = args[3]
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\":\"Marble does not exist: " + id + "\"}"
return shim.Error(jsonResp)
}
var ar ApproveReject
request := Request{RequestedFrom: requestFrom, Id: id, requestId: requestid, Quantity: quantity, Status: "initiated"}
ar.Decision = append(ar.Decision, request)
requestasBytes, err := json.Marshal(ar)
if err != nil {
return shim.Error(err.Error())
}
// === Save marble to state ===
err = stub.PutState(requestFrom, requestasBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment