1079 lines
38 KiB
Go
1079 lines
38 KiB
Go
/*
|
|
Copyright IBM Corp. 2016 All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
|
|
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
|
|
//to be modified as well with the new ID of chaincode_example02.
|
|
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
|
|
//hard-coding.
|
|
|
|
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 owner struct {
|
|
ObjectType string `json:"docType"`
|
|
UserId string `json:"userId"`
|
|
UserName string `json:"userName"`
|
|
UserFirstName string `json:"userFirstName"`
|
|
// Mail string `json:"userMail"`
|
|
UserPhone string `json:"userPhone"`
|
|
// Address string `json:"userAddress"`
|
|
// PostalCode string `json:"userPostalCode"`
|
|
// City int `json:"userCity"`
|
|
UserAssociation string `json:"userAssociation"`
|
|
UserAuthorized bool `json:"userAuthorized"`
|
|
}
|
|
|
|
type wallet struct {
|
|
ObjectType string `json:"docType"` //docType is used to distinguish the various type$
|
|
Id string `json:"id"` //the fieldtags are needed to keep case from bounci$
|
|
WalletType string `json:"walletType"`
|
|
Sold float64 `json:"sold"`
|
|
Owner string `json:"owner"`
|
|
}
|
|
|
|
|
|
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
|
|
fmt.Println("ex02 Init")
|
|
_, args := stub.GetFunctionAndParameters()
|
|
var A string // Entities
|
|
var Aval int // Asset holdings
|
|
var err error
|
|
|
|
if len(args) != 2 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 2")
|
|
}
|
|
|
|
// Initialize the chaincode
|
|
A = args[0]
|
|
Aval, err = strconv.Atoi(args[1])
|
|
if err != nil {
|
|
return shim.Error("Expecting integer value for asset holding")
|
|
}
|
|
fmt.Printf("Aval = %d\n", Aval)
|
|
|
|
// Write the state to the ledger
|
|
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
return shim.Success(nil)
|
|
}
|
|
|
|
|
|
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
|
|
fmt.Println("ex02 Invoke")
|
|
function, args := stub.GetFunctionAndParameters()
|
|
if function == "invoke" {
|
|
// Make payment of X units from A to B
|
|
return t.invoke(stub, args)
|
|
} else if function == "delete" {
|
|
// Deletes an entity from its state
|
|
return t.delete(stub, args)
|
|
} else if function == "query" {
|
|
// the old "Query" is now implemtned in invoke
|
|
return t.query(stub, args)
|
|
} else if function == "register" {
|
|
// register a new user into the ledger
|
|
return t.register(stub, args)
|
|
} else if function == "registerUser" {
|
|
// initiate a new account
|
|
return t.registerUser(stub, args)
|
|
} else if function == "readUser" {
|
|
// query a specific user from the ledger
|
|
return t.readUser(stub, args)
|
|
} else if function == "deleteUser" {
|
|
// delete a specific user from the ledger
|
|
return t.deleteUser(stub, args)
|
|
} else if function == "setUserPermission" {
|
|
// change the permission of an account
|
|
return t.setUserPermission(stub, args)
|
|
} else if function == "initWallet" {
|
|
// initiate a new wallet
|
|
return t.initWallet(stub, args)
|
|
} else if function == "readWallet" {
|
|
// query a specific wallet from the ledger
|
|
return t.readWallet(stub, args)
|
|
} else if function == "deleteWallet" {
|
|
// delete a specific wallet from the ledger
|
|
return t.deleteWallet(stub, args)
|
|
} else if function == "transferWallet" {
|
|
// change the owner of a specific wallet
|
|
return t.transferWallet(stub, args)
|
|
} else if function == "setSoldOnWallet" {
|
|
// set a new sold to a wallet
|
|
return t.setSoldOnWallet(stub, args)
|
|
} else if function == "transaction" {
|
|
// make a transaction of X units from a wallet to an other
|
|
return t.transaction(stub, args)
|
|
} else if function == "queryWalletsByOwner" {
|
|
// make a transaction of X units from a wallet to an other
|
|
return t.queryWalletsByOwner(stub, args)
|
|
} else if function == "queryWalletsByType" {
|
|
// make a transaction of X units from a wallet to an other
|
|
return t.queryWalletsByType(stub, args)
|
|
} else if function == "queryWallets" {
|
|
// make a transaction of X units from a wallet to an other
|
|
return t.queryWallets(stub, args)
|
|
} else if function == "getHistoryForWallet" {
|
|
// make a transaction of X units from a wallet to an other
|
|
return t.getHistoryForWallet(stub, args)
|
|
} else if function == "getAllWallets" {
|
|
// get All Wallets
|
|
return t.getAllWallets(stub, args)
|
|
}
|
|
|
|
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\" \"register\"")
|
|
}
|
|
|
|
|
|
// Transaction makes payment of X units from A to B
|
|
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
var A, B string // Entities
|
|
var Aval, Bval int // Asset holdings
|
|
var X int // Transaction value
|
|
var err error
|
|
|
|
if len(args) != 3 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 3")
|
|
}
|
|
|
|
A = args[0]
|
|
B = args[1]
|
|
|
|
// Get the state from the ledger
|
|
// TODO: will be nice to have a GetAllState call to ledger
|
|
Avalbytes, err := stub.GetState(A)
|
|
if err != nil {
|
|
return shim.Error("Failed to get state")
|
|
}
|
|
if Avalbytes == nil {
|
|
return shim.Error("Entity not found")
|
|
}
|
|
Aval, _ = strconv.Atoi(string(Avalbytes))
|
|
|
|
Bvalbytes, err := stub.GetState(B)
|
|
if err != nil {
|
|
return shim.Error("Failed to get state")
|
|
}
|
|
if Bvalbytes == nil {
|
|
return shim.Error("Entity not found")
|
|
}
|
|
Bval, _ = strconv.Atoi(string(Bvalbytes))
|
|
|
|
// Perform the execution
|
|
X, err = strconv.Atoi(args[2])
|
|
if err != nil {
|
|
return shim.Error("Invalid transaction amount, expecting a integer value")
|
|
}
|
|
Aval = Aval - X
|
|
Bval = Bval + X
|
|
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
|
|
|
|
// Write the state back to the ledger
|
|
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
return shim.Success(nil)
|
|
}
|
|
|
|
|
|
// Deletes an entity from state
|
|
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
if len(args) != 1 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 1")
|
|
}
|
|
|
|
A := args[0]
|
|
|
|
// Delete the key from the state in ledger
|
|
err := stub.DelState(A)
|
|
if err != nil {
|
|
return shim.Error("Failed to delete state")
|
|
}
|
|
|
|
return shim.Success(nil)
|
|
}
|
|
|
|
|
|
// query callback representing the query of a chaincode
|
|
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
var A string // Entities
|
|
var err error
|
|
|
|
if len(args) != 1 {
|
|
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
if Avalbytes == nil {
|
|
jsonResp := "{\"Error\":\"Nil amount 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) register(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
var A string // Entities
|
|
var Aval int // Asset holdings
|
|
var err error
|
|
|
|
if len(args) != 2 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 2")
|
|
}
|
|
|
|
// Initialize the chaincode
|
|
A = args[0]
|
|
Aval, err = strconv.Atoi(args[1])
|
|
if err != nil {
|
|
return shim.Error("Expecting integer value for asset holding")
|
|
}
|
|
fmt.Printf("Aval = %d\n", Aval)
|
|
|
|
// Write the state to the ledger
|
|
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
return shim.Success(nil)
|
|
}
|
|
|
|
|
|
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")
|
|
}
|
|
//if len(args[5]) <= 0 {
|
|
// return shim.Error("6th 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")
|
|
}
|
|
//if len(args[3]) <= 0 {
|
|
// return shim.Error("4th argument must be a non-empty string")
|
|
//}
|
|
//if len(args[4]) <= 0 {
|
|
// return shim.Error("4th argument must be a non-empty string")
|
|
//}
|
|
|
|
walletId := args[0]
|
|
walletType := strings.ToLower(args[1])
|
|
owner := args[2]
|
|
sold := 0.0
|
|
//authorized_by_association := args[4]
|
|
|
|
if err != nil {
|
|
return shim.Error("3rd argument must be a numeric string")
|
|
}
|
|
|
|
//if owner.UserAssociation != authorized_by_association {
|
|
// return shim.Error("The association '" + authorized_by_association + "'cannot authorize creation for '" + owner.UserId + "'.")
|
|
//}
|
|
|
|
// ==== 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, sold, 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 {
|
|
|
|
// 0 1
|
|
// "name", "bob"
|
|
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) setSoldOnWallet(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
|
|
// 0 1
|
|
// "name", "bob"
|
|
if len(args) < 2 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 2")
|
|
}
|
|
|
|
walletId := args[0]
|
|
newSold, err := strconv.ParseFloat(args[1], 64)
|
|
fmt.Println("- start setSoldOnWallet ", walletId, newSold)
|
|
|
|
// ==== 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.Sold = walletToSet.Sold + newSold //change the sold
|
|
|
|
walletJSONasBytes, _ := json.Marshal(walletToSet)
|
|
err = stub.PutState(walletId, walletJSONasBytes) //rewrite the wallet
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
fmt.Println("- end setSoldOnWallet (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)
|
|
}
|
|
|
|
walletSoldToSet := wallet{}
|
|
err = json.Unmarshal(walletAsBytes, &walletSoldToSet) //unmarshal it aka JSON
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
if walletSoldToSet.Sold <= 0 {
|
|
return shim.Error("This wallet is not allowed to make a transaction:" + walletId)
|
|
} else {
|
|
walletSoldToSet.Sold = walletSoldToSet.Sold - transactionValue //change the sold
|
|
}
|
|
|
|
walletJSONasBytes, _ := json.Marshal(walletSoldToSet)
|
|
err = stub.PutState(walletId, walletJSONasBytes) //rewrite the wallet
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
walletTargetSoldToSet := wallet{}
|
|
err = json.Unmarshal(walletTargetAsBytes, &walletTargetSoldToSet) //unmarshal it aka JSON
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
walletTargetSoldToSet.Sold = walletTargetSoldToSet.Sold + transactionValue
|
|
|
|
walletTargetJSONasBytes, _ := json.Marshal(walletTargetSoldToSet)
|
|
err = stub.PutState(walletTargetId, walletTargetJSONasBytes) //rewrite the wallet
|
|
if err != nil {
|
|
return shim.Error(err.Error())
|
|
}
|
|
|
|
fmt.Println("- end setSoldOnWallet (success)")
|
|
return shim.Success(nil)
|
|
}
|
|
|
|
|
|
func (t *SimpleChaincode) queryWalletsByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
|
|
|
// 0
|
|
// "bob"
|
|
if len(args) < 1 {
|
|
return shim.Error("Incorrect number of arguments. Expecting 1")
|
|
}
|
|
|
|
owner := strings.ToLower(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 {
|
|
|
|
// 0
|
|
// "bob"
|
|
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 {
|
|
|
|
// 0
|
|
// "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) getAllWallets(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("- getAllWallets queryResult:\n%s\n", buffer.String())
|
|
|
|
return shim.Success(buffer.Bytes())
|
|
}
|
|
|
|
|
|
func main() {
|
|
err := shim.Start(new(SimpleChaincode))
|
|
if err != nil {
|
|
fmt.Printf("Error starting Simple chaincode: %s", err)
|
|
}
|
|
}
|
|
|
|
|
|
// ===========================================================================================
|
|
// constructQueryResponseFromIterator constructs a JSON array containing query results from
|
|
// a given result iterator
|
|
// ===========================================================================================
|
|
func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) (*bytes.Buffer, error) {
|
|
// 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 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("]")
|
|
|
|
return &buffer, nil
|
|
}
|
|
|
|
|
|
// =========================================================================================
|
|
// getQueryResultForQueryString executes the passed in query string.
|
|
// Result set is built and returned as a byte array containing the JSON results.
|
|
// =========================================================================================
|
|
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, err := constructQueryResponseFromIterator(resultsIterator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
|
|
|
|
return buffer.Bytes(), nil
|
|
}
|
|
|