Chain code development marbles management
- 1. overview
- 2. marble marbles management
- 3 build local test environment and test chain code
- 3.1 mount chain code
- 3.2 start Network Environment
- 3.3 enter chain code container to compile chain code to provide chain code service
- 3.4 enter cli container to install chain code instantiation chain code
- 3.5 test chain code
- 4 turn off the network
1. overview
According to the introduction of the previous ten articles, I have basically understood the network environment of fabric and the process of development, deployment and debugging of chain code. This article, once again consolidate chain code development and deployment call debugging.
2. marble marbles management
marble
// Declaration bullet structure type Marble struct { // object type ObjectType string `json:"objectType"` // Marbles name Name string `json:"name"` // Pearl color Color string `json:"color"` // Marbles size Size int `json:"size"` // Marbles owner Owner string `json:"owner"` }
2.1 implementation function
- Marbles creation
- Marbles inquiry
- Marbles removal
- Marbles Trading
- Marble operation history query
- Query a user's marbles list
2.2chaincode chain code
markbels.go
package main // Introduce dependency package import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" "time" "bytes" "strconv" "encoding/json" ) // Declaration structure type MarblesChaincode struct{ } // Declaration bullet structure type Marble struct { // object type ObjectType string `json:"objectType"` // Marbles name Name string `json:"name"` // Pearl color Color string `json:"color"` // Marbles size Size int `json:"size"` // Marbles owner Owner string `json:"owner"` } // Instantiation chain code interface func (t *MarblesChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { // No operation for initialization here fmt.Println("MarblesChaincode Chain code instantiation") return shim.Success(nil) } // invoke operation func (t *MarblesChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { // Get the methods and parameters of the call fn, args := stub.GetFunctionAndParameters() // Judge distribution business method if fn == "initMarble" { // Call create marbles method return t.initMarble(stub,args) }else if fn == "readMarble" { // Call to read marbles information return t.readMarble(stub,args) }else if fn == "deleteMarble" { // Call to delete marble information return t.deleteMarble(stub,args) }else if fn == "transferMarble" { // Call transaction marbles method return t.transferMarble(stub,args) }else if fn == "getMarbleByRange" { // Call scope query return t.getMarbleByRange(stub,args) }else if fn == "queryMarblesByOwner" { // Call to query the marble information owned by the user return t.queryMarblesByOwner(stub,args) }else if fn == "queryHistoryForMarble" { // Query the historical operation information of marbles return t.queryHistoryForMarble(stub,args) } // If no corresponding method returns an error return shim.Error(fn +" Method does not exist!") } // Create marble args: name color size owner func (t *MarblesChaincode) initMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // Judge whether the marble already exists according to the name name := args[0] // Find if there is a marble named name before marbleBytes, err := stub.GetState(name) // query error if err != nil { return shim.Error(err.Error()) } // If marbleBytes already exists if marbleBytes != nil { return shim.Error("be known as"+ name + "Our marbles already exist"); } // Write to ledger if not present color := args[1] size,err := strconv.Atoi(args[2]) owner := args[3] // Assembly and measurement structure marble := &Marble{"marble",name,color,size,owner} // Convert marble to json string and store it in the ledger marbleJsonStr, err := json.Marshal(marble) if err != nil { return shim.Error(err.Error()) } // PutState json information written into the ledger err = stub.PutState(name,marbleJsonStr) if err != nil { return shim.Error(err.Error()) } fmt.Println("Create marbles successfully!!") // Create key combination for query at the same time indexName := "owner~record" indexKey, err := stub.CreateCompositeKey(indexName,[]string{ owner,string(marbleJsonStr)}) if err != nil{ return shim.Error(err.Error()) } err = stub.PutState(indexKey,[]byte{0x00}) if err != nil{ return shim.Error(err.Error()) } fmt.Println(indexKey) return shim.Success(nil) } // Read marble args: marblename func (t *MarblesChaincode) readMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // Get the name whose parameter is marble name := args[0] // Read the data of marble according to name marbleBytes, err := stub.GetState(name) if err != nil { return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(name + " The marble information of does not exist") } // Return information return shim.Success(marbleBytes) } // Delete marble args: marbleName func (t *MarblesChaincode) deleteMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // Get the name of the marker from the parameter total name := args[0] // Judge whether the marbles exist marbleBytes, err := stub.GetState(name) if err != nil { return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(name + "The marble information of does not exist") } // Delete marbles err = stub.DelState(name) if err != nil { return shim.Error(err.Error()) } fmt.Println(name + " Marbles deleted successfully!") return shim.Success(nil) } // Transaction: marblename newowner func (t *MarblesChaincode) transferMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // Get parameters marbleName := args[0] newOwner := args[1] // Check for marbles marbleBytes, err := stub.GetState(marbleName) if err != nil{ return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(marbleName + " The marble information of does not exist") } // Turn information in the ledger into a Marble structure marbleInfo := Marble{} err = json.Unmarshal(marbleBytes,&marbleInfo) if err != nil { return shim.Error(err.Error()) } // Modify owner marbleInfo.Owner = newOwner // Convert to json data newMarbleBytes,err := json.Marshal(marbleInfo) if err != nil { return shim.Error(err.Error()) } // Write account err = stub.PutState(marbleName,newMarbleBytes) if err != nil { return shim.Error(err.Error()) } fmt.Println(marbleName +"Transfer to"+ newOwner+ "'s marbles deal completed") return shim.Success(nil) } // Query marble args in a range: startMarble endMarble func (t *MarblesChaincode) getMarbleByRange(stub shim.ChaincodeStubInterface, args []string) peer.Response { // Get parameters startMarble := args[0] endMarble := args[1] // Call query method resultIterator, err := stub.GetStateByRange(startMarble,endMarble) if err!=nil { return shim.Error(err.Error()) } defer resultIterator.Close(); var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false // Traverse resultIterator for resultIterator.HasNext() { item,err := resultIterator.Next() if err!= nil { return shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString("{\"key\":") buffer.WriteString("\""+item.Key+"\"") buffer.WriteString(",\"record\":") buffer.WriteString(string(item.Value)) buffer.WriteString("}") isWriteSplit = true } buffer.WriteString("]") // Return result return shim.Success(buffer.Bytes()) } // Query the marble information owned by users func (t *MarblesChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface,args []string) peer.Response { owner := args[0] fmt.Println("Start query user"+owner+"Marble information you have") indexName := "owner~record" resultIterator,err := stub.GetStateByPartialCompositeKey(indexName ,[]string{owner}) if err != nil { return shim.Error(err.Error()) } defer resultIterator.Close(); var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false for resultIterator.HasNext() { item,_ := resultIterator.Next() _,compositeKeyParts,err := stub.SplitCompositeKey(item.Key) if err != nil { shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString(compositeKeyParts[1]) isWriteSplit = true } buffer.WriteString("]") // Return result return shim.Success(buffer.Bytes()) // Get parameters. The following is to query through couchdb. However, if my local environment starts couchdb, the CLI container cannot connect to couchdb when the container starts. Guess that couchdb starts unfinished cli in the container and tries to connect. // owner := args[0] // fmt.Println("start query user" + owner + "owned marbles information") // queryString := fmt.Sprintf("{\"selector\":{\"owner\": %s }}",owner) // resultIterator,err := stub.GetQueryResult(queryString) // if err != nil { // return shim.Error(err.Error()) // } // defer resultIterator.Close(); // var buffer bytes.Buffer // buffer.WriteString("[") // isWriteSplit := false // //Traverse resultIterator // for resultIterator.HasNext() { // item,err := resultIterator.Next() // if err!= nil { // return shim.Error(err.Error()) // } // if isWriteSplit==true { // buffer.WriteString(",") // } // buffer.WriteString("{\"key\":") // buffer.WriteString("\""+item.Key+"\"") // buffer.WriteString(",\"record\":") // buffer.WriteString(string(item.Value)) // buffer.WriteString("}") // isWriteSplit = true // } // buffer.WriteString("]") // //Return results // return shim.Success(buffer.Bytes()) } // Query users func (t *MarblesChaincode) queryHistoryForMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // Get parameters marbleName := args[0] // Get historical information resultIterator,err := stub.GetHistoryForKey(marbleName) if err != nil { return shim.Error(err.Error()) } defer resultIterator.Close() var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false // Traverse resultIterator for resultIterator.HasNext() { item,err := resultIterator.Next() if err!= nil { return shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString("{\"TxId\":") buffer.WriteString("\""+item.TxId+"\"") buffer.WriteString(",\"Timestamp\":") buffer.WriteString(time.Unix(item.Timestamp.Seconds,int64(item.Timestamp.Nanos)).String()) buffer.WriteString(",\"Value\":") buffer.WriteString(string(item.Value)) buffer.WriteString(",\"IsDelete\":") buffer.WriteString(strconv.FormatBool(item.IsDelete)) buffer.WriteString("}") isWriteSplit = true } buffer.WriteString("]") // If no corresponding method returns an error return shim.Success(buffer.Bytes()) } // main function func main (){ err := shim.Start(new (MarblesChaincode)) if err != nil { fmt.Printf("Error start MarblesChaincode") } }
2.3 write test class
markbels_test.go
package main import( "fmt" "testing" "github.com/hyperledger/fabric/core/chaincode/shim" ) func checkInit(t *testing.T,stub *shim.MockStub,args [][]byte) { res := stub.MockInit("1",args) if (res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } } func checkInvoke(t *testing.T,stub *shim.MockStub,args [][]byte) { res := stub.MockInvoke("1",args) if (res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } } func checkReadMarble(t *testing.T,stub *shim.MockStub, name string) { res := stub.MockInvoke("1",[][]byte{[]byte("readMarble"),[]byte(name)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarble",name,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkReadMarbleByRange(t *testing.T,stub *shim.MockStub, startKey string,endKey string) { res := stub.MockInvoke("1",[][]byte{[]byte("getMarbleByRange"),[]byte(startKey),[]byte(endKey)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarbleByRange",startKey,endKey,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkQueryMarblesByOwner(t *testing.T,stub *shim.MockStub, owner string) { res := stub.MockInvoke("1",[][]byte{[]byte("queryMarblesByOwner"),[]byte(owner)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkQueryMarblesByOwner",owner,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkQueryMarblesHistoryByKey(t *testing.T,stub *shim.MockStub, marbleName string) { res := stub.MockInvoke("1",[][]byte{[]byte("queryHistoryForMarble"),[]byte(marbleName)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarbleByRange",marbleName,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func Test_MarblesChaincode(t *testing.T) { hello := new(MarblesChaincode) stub := shim.NewMockStub("marble",hello) checkInit(t,stub,nil) // name color size owner checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-1"),[]byte("red"),[]byte("10"),[]byte("LH")}) checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-2"),[]byte("yellow"),[]byte("11"),[]byte("LH")}) checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-3"),[]byte("blue"),[]byte("12"),[]byte("WX")}) checkQueryMarblesByOwner(t,stub,"WX") // checkReadMarble(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("transferMarble"),[]byte("marble-1"),[]byte("WX")}) // checkInvoke(t,stub,[][]byte{[]byte("transferMarble"),[]byte("marble-1"),[]byte("LH")}) // checkInvoke(t,stub,[][]byte{[]byte("deleteMarble"),[]byte("marble-1")}) // checkQueryMarblesHistoryByKey(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-4"),[]byte("green"),[]byte("12"),[]byte("WX")}) // checkReadMarbleByRange(t,stub,"marble-1","marble-4") // checkReadMarble(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-1"),[]byte("red"),[]byte("10"),[]byte("LH")}) // checkReadMarble(t,stub,"marble-2") }
2.4 running test
Go to the directory where marbles.go and marbles'ou test.go are located
I put it in the $gopath / my "chaincode / marbles directory
cd $GOPATH/my_chaincode/marbles go test marbles_test.go -v marbles.go --tags=nopkcs11
Try modifying marbles test.go to test other methods
3 build local test environment and test chain code
Using the chaincode docker DevMode environment test in the fabric samples project
3.1 mount chain code
Move the written marbles.go and marbles_test.go files to
$GOAPTH/src/github.com/hyperledger/fabric-samples/chaincode
Easy to mount in container
3.2 start Network Environment
Go to the chaincode docker DevMode directory
cd $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmode
Starting network environment with docker compose
docker-compose -f docker-compose-simple.yaml up
List of containers after startup
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a7b22f7351fa hyperledger/fabric-tools "/bin/bash -c ./scri..." 7 seconds ago Up 5 seconds cli d761b06a034a hyperledger/fabric-ccenv "/bin/bash -c 'sleep..." 7 seconds ago Up 5 seconds chaincode 7b329ef1311f hyperledger/fabric-peer "peer node start --p..." 8 seconds ago Up 6 seconds 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer 6302ffa111e2 hyperledger/fabric-orderer "orderer" 9 seconds ago Up 7 seconds 0.0.0.0:7050->7050/tcp orderer
3.3 enter chain code container to compile chain code to provide chain code service
Start a new terminal
Container entry
docker exec -it chaincode /bin/bash
Go to the mount go file directory
cd marbles/
Compiler
go build
Start the chain code service after compilation
CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./marbles
3.4 enter cli container to install chain code instantiation chain code
Start a new terminal
Enter the cli container
docker exec -it cli /bin/bash
Chain code installation
peer chaincode install -p chaincodedev/chaincode/marbles -n mycc -v 0
Initialization chain code
peer chaincode instantiate -n mycc -v 0 -c '{"Args":[]}' -C myc
3.5 test chain code
Create three marbles
peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-1","red","10","LH"]}' -C myc peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-2","yellow","11","LH"]}' -C myc peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-3","blue","12","WX"]}' -C myc
Inquire for marbles
peer chaincode query -n mycc -c '{"Args":["readMarble","marble-1"]}' -C myc
Range query
peer chaincode query -n mycc -c '{"Args":["getMarbleByRange","marble-1","marble-3"]}' -C myc
Transfer marble
peer chaincode invoke -n mycc -c '{"Args":["transferMarble","marble-1","WX"]}' -C myc
Query marble-1 again: whether owner changes to WX
peer chaincode query -n mycc -c '{"Args":["readMarble","marble-1"]}' -C myc
Query all marbles of the owner
peer chaincode query -n mycc -c '{"Args":["queryMarblesByOwner","WX"]}' -C myc
View the modification history of marbles: one initialization transaction and one transfer transaction can be found in the result
peer chaincode query -n mycc -c '{"Args":["queryHistoryForMarble","marble-1"]}' -C myc
4 turn off the network
Press control+c twice at the terminal that starts the network
docker-compose -f docker-compose-simple.yaml down
Stop and close all containers