In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/03 Report--
HyperLeger Fabric development (7)-- HyperLeger Fabric chain code development 1. Chain code development mode 1. Introduction to chain code development mode
The chain code development and debugging of Fabric is more tedious. Without the chain code development mode, the chain code cannot be tested locally, and after it must be deployed to docker,install and instantiate, the Peer node will launch the chain code in the new container. However, you can only view the chain code log through docker logs and debug the chain code by printing the log. If the chain code is modified, you need to restart the above process.
In order to simplify the debugging process of Fabric chain code development, Fabric introduces the chain code development mode. Normally, the chain code is initiated and maintained by the Peer node, but in the chain code development mode, the chain code is built and started by the user. The chain code development mode is used for the rapid conversion in the chain code life cycle stage, such as chain code coding, construction, operation, debugging and so on.
Using the chain code development mode, starting the Peer node still needs to install and initialize the chain code, but it only needs to be executed once, and the chain code can be run locally (such as starting directly in IDE), and the debugging function of IDE can be used. If the chain code is modified, the modified chain code can be seen in the Peer node if it is compiled and run directly in IDE.
To use chain code development mode, first modify the startup command that runs the Peer node container, adding the-- peer-chaincodedev parameter, for example, in docker-compose.yaml:
Command: peer node start-- peer-chaincodedev=true
Specify the mapping between the host port and the Peer node container port:
Ports:-7052
The host port is the port used when the chain code is started locally to connect to the Peer node. Fabric version 1.1 uses port 7052, and if it is Fabric version 1.0, use port 7051. You can modify the default port by modifying the core.yaml file.
Enter the cli container, install and instantiate the chain code:
Peer chaincode install-n chain code name-v 1-p xxx.com/xxxapppeer chaincode instantiate-o orderer.example.com:7050-C mychannel-n chain code name-v version number-c'{"Args": [""]}'- P "OR ('Org1MSP.member','Org2MSP.member')"
The endorsement policy is specified using the-P parameter.
If you run the chain code directly in IDE, you need to configure two environment variables first:
CORE_PEER_ADDRESS=127.0.0.1:7052 CORE_CHAINCODE_ID_NAME= chain code name: version number
2. Deployment of chain code development environment
The fabric-sample project provides several examples of Fabric development, one of which provides a chain code development Fabric network environment, that is, chaincode-docker-devmode examples.
The fabric-sample project address is as follows:
Https://github.com/hyperledger/fabric-samples
Enter the chaincode-docker-devmode directory:
Cd fabric-samples/chaincode-docker-devmode
Launch the Fabric chain code development network environment:
Docker-compose- f docker-compose-simple.yaml up-d
The docker-compose-simple.yaml file specifies the chain code injection directory as. /.. / chaincode in the chaincode container, which is used to specify the developer's development directory.
3. Compiled chain code
Enter the chain code container:
Docker exec-it chaincode bash
At this point, enter the working directory of the chain code container, where the chain code developed by the developer is stored.
Compile chain code:
Cd [chain code directory] go build-o [executable]
Deploy chain code
CORE_PEER_ADDRESS=peer: [Port number] CORE_CHAINCODE_ID_NAME= [chain code example]: 0. / [executable file]
Exit the chain code container:
Exit
4. Test the chain code
Enter the client cli container:
Docker exec-it cli bash
Installation chain code
Cd.. peer chaincode install-p [directory path of chain code executable file]-n [chain code instance]-v [version number]
Instantiate chain code
Peer chaincode instantiate-n [chain code instance]-v [version number]-c'{"Args": ["function", "parameter"]}'- C [channel]
Call chain code
Peer chaincode invoke-n [chain code example]-c'{"Args": ["function", "parameter", "parameter"]}'- C [channel]
5. Test the new chain code
If you want to test the newly developed chain code, you need to add the newly developed chain code directory to the chaincode subdirectory and restart the chaincode-docker-devmode network.
Second, the structure of chain code 1, chain code API
Each chain code program must implement the chain code interface, and the methods in the interface will be called in response to the incoming transaction. The Init method is called when the chain code receives an instantiate (instantiation) or upgrade (upgrade) transaction to perform the necessary initialization operations, including initializing the state of the application; the Invoke method is called in response to the invoked transaction to execute the transaction.
The chain code needs to implement the chain code interface in the development process, and the type of transaction determines which interface function will be called. For example, the instantiate and upgrade types will call the chain code Init interface, while the invoke type transaction will call the chain code Invoke interface. The API for chain code is defined as follows:
Type Chaincode interface {Init (stub ChaincodeStubInterface) pb.Response Invoke (stub ChaincodeStubInterface) pb.Response}
Shim.ChaincodeStubInterface interface is used to access and modify account books, and to call each other between chain codes, which provides a large number of practical methods for writing chain code business logic.
2. The basic structure of chain code.
The necessary structure of the chain code is as follows:
Package main// introduces the necessary package import ("github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer") / / declares a structure type SimpleChaincode struct {} / / adds an Init method to the structure func (t * SimpleChaincode) Init (stub shim.ChaincodeStubInterface) pb.Response {/ / implements chain code initialization or upgrade processing logic in this method / / flexible use of stub when writing API} / / add the Invoke method func (t * SimpleChaincode) Invoke (stub shim.ChaincodeStubInterface) pb.Response {/ / in this method to realize the processing logic when the chain code is called or queried / / you can flexibly use the API} / / main function in stub when writing Need to call the shim.Start () method func main () {err:=shim.Start (new (SimpleChaincode)) if err! = nil {fmt.Printf ("Error starting Simple chaincode:% s", err)}}
To develop chain code in GE language, you need to define a struct, then define two functions, Init and Invoke, on struct, and define main function as the starting entry of chain code.
Both Init and Invoke have an incoming parameter stub shim.ChaincodeStubInterface, which provides a large number of practical methods for writing chain code business logic.
3. Shim.ChaincodeStubInterface interface 1. Get the calling parameters.
GetArgs () [] [] byte
Get the passed-in parameter list as an array of byte arrays
GetStringArgs () [] string
Get the passed-in parameter list in the form of a string array
GetFunctionAndParameters () (string, [] string)
The parameters of the string array are divided into two parts. The first word of the array is Function, and the rest is Parameter.
GetArgsSlice () ([] byte, error)
Get the parameter list in the form of byte slices
Function, args: = stub.GetFunctionAndParameters ()
2. Add, delete, modify and check State DB
The core business logic of chain code development is the addition, deletion, modification and query of State Database.
PutState (key string, value [] byte) error
State DB is a Key Value database, adding and modifying data is a unified operation, if the specified Key already exists in the database, then it is a modify operation, if the Key does not exist, then it is an insert operation. Key is a string, and Value is a string of objects serialized by JSON.
Type Student struct {Id int Name string} func (t * SimpleChaincode) testStateOp (stub shim.ChaincodeStubInterface, args [] string) pb.Response {student1:=Student {1, "Devin Zeng"} key:= "Student:" + strconv.Itoa (student1.Id) / / Key format is Student: {Id} studentJsonBytes, err: = json.Marshal (student1) / / Json serial number if err! = nil {return shim.Error (err.Error ())} err= stub.PutState (key) StudentJsonBytes) if (erratic roomnil) {return shim.Error (err.Error ())} return shim.Success ([] byte ("Saved Student!"))}
DelState (key string) error
Delete State DB data according to Key. If the corresponding data cannot be found according to Key, the deletion fails.
Err= stub.DelState (key) if err! = nil {return shim.Error ("Failed to delete Student from DB, key is:" + key)}
GetState (key string) ([] byte, error)
Query the database according to Key and return the byte array data, which needs to be converted to string, and then deserialized by Json to get the object.
Cannot GetState immediately after PutState in a chain code function, because it is not finished and has not been submitted to StateDB.
DbStudentBytes,err:= stub.GetState (key) var dbStudent Student;err=json.Unmarshal (dbStudentBytes,&dbStudent) / / deserialization if err! = nil {return shim.Error ("{\" Error\ ":\" Failed to decode JSON of: "+ string (dbStudentBytes) +"\ "to Student}")} fmt.Println ("Read Student from DB, name:" + dbStudent.Name) 3, compound key processing
CreateCompositeKey (objectType string, attributes [] string) (string, error)
To generate a compound key from an object, you need to specify the type of the object and the properties involved in the compound key.
Type ChooseCourse struct {CourseNumber string / / course No. StudentId int / / Student ID Confirm bool / / confirm} cc:=ChooseCourse {"CS101", 123 CS101} var key1,_= stub.CreateCompositeKey ("ChooseCourse", [] string {cc.CourseNumber,strconv.Itoa (cc.StudentId)}) fmt.Println (key1)
SplitCompositeKey (compositeKey string) (string, [] string, error)
Split the object type and attribute string array according to the compound key
ObjType,attrArray,_:= stub.SplitCompositeKey (key1) fmt.Println ("Object:" + objType+ ", Attributes:" + strings.Join (attrArray, "|") GetStateByPartialCompositeKey (objectType string, keys [] string) (StateQueryIteratorInterface, error)
Queries that match prefixes with Key are not allowed to match using compound keys in the latter part.
4. Obtain the current user certificate
GetCreator () ([] byte, error)
Obtain the user certificate of the client that calls this chain code.
By obtaining the user certificate of the current user, you can convert the string of the user certificate into a Certificate object, and then get the name of the current user through Subject.
Func (t * SimpleChaincode) testCertificate (stub shim.ChaincodeStubInterface, args [] string) pb.Response {creatorByte,_:= stub.GetCreator () certStart: = bytes.IndexAny (creatorByte, "- BEGIN") if certStart =-1 {fmt.Errorf ("No certificate found")} certText: = creatorByte [certStart:] bl, _: = pem.Decode (certText) if bl = = nil {fmt.Errorf ("Could not decode the PEM structure")} cert Err: = x509.ParseCertificate (bl.Bytes) if err! = nil {fmt.Errorf ("ParseCertificate failed")} uname:=cert.Subject.CommonName fmt.Println ("Name:" + uname) return shim.Success ([] byte ("Called testCertificate" + uname))} 5, advanced query
GetStateByRange (startKey, endKey string) * * (StateQueryIteratorInterface, error)
Provides an interface for querying the Key of a certain interval, which is suitable for any State DB and returns a StateQueryIteratorInterface interface. You need to do another for loop through the return interface to read the returned information.
Func getListResult (resultsIterator shim.StateQueryIteratorInterface) ([] byte,error) {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 ("queryResult:\ n% s\ n", buffer.String ()) return buffer.Bytes (), nil} func (t * SimpleChaincode) testRangeQuery (stub shim.ChaincodeStubInterface, args [] string) pb.Response {resultsIterator,err:= stub.GetStateByRange ("Student:1") "Student:3") if erratic getListResult failed nil {return shim.Error ("Query by Range failed")} students,err:=getListResult (resultsIterator) if erratic roomnil {return shim.Error ("getListResult failed")} return shim.Success (students)}
GetQueryResult (query string) (StateQueryIteratorInterface, error)
Rich query, CouchDB can be used.
Func (t * SimpleChaincode) testRichQuery (stub shim.ChaincodeStubInterface, args [] string) pb.Response {name:= "Lee" queryString: = fmt.Sprintf ("{\" selector\ ": {\" Name\ ":\"% s\ "}}", name) resultsIterator,err:= stub.GetQueryResult (queryString) / / must be CouchDB to if erratic roomnil {return shim.Error ("Rich query failed")} students Err:=getListResult (resultsIterator) if erratic roomnil {return shim.Error ("Rich query failed")} return shim.Success (students)}
GetHistoryForKey (key string) (HistoryQueryIteratorInterface, error)
Changes to the same data (the same Key) are recorded in the block chain. You can use the GetHistoryForKey method to obtain the change history recorded by the object in the block chain, including which TxId it is in, the data modified, the timestamp of the modification, and whether it is deleted.
Func (t * SimpleChaincode) testHistoryQuery (stub shim.ChaincodeStubInterface, args [] string) pb.Response {student1:=Student {1, "Lee"} key:= "Student:" + strconv.Itoa (student1.Id) it,err:= stub.GetHistoryForKey (key) if erratic roomnil {return shim.Error (err.Error ())} var result,_= getHistoryListResult (it) return shim.Success (result)} func getHistoryListResult (resultsIterator shim.HistoryQueryIteratorInterface) ([] byte Error) {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 (",")} item _: = json.Marshal (queryResponse) buffer.Write (item) bArrayMemberAlreadyWritten = true} buffer.WriteString ("]") fmt.Printf ("queryResult:\ n% s\ n", buffer.String ()) return buffer.Bytes (), nil} 6, call chain code
InvokeChaincode (chaincodeName string, args [] [] byte, channel string) pb.Response
Call the chain code that has been deployed on other channels in this chain code.
Channel: Channel name
ChaincodeName: chain code instance name
Args: array combination of called methods and parameters
Func (t * SimpleChaincode) testInvokeChainCode (stub shim.ChaincodeStubInterface, args [] string) pb.Response {trans:= [] [] byte {[] byte ("invoke"), [] byte ("a"), [] byte ("b"), [] byte ("11")} response:= stub.InvokeChaincode ("mycc", trans, "mychannel") fmt.Println (response.Message) return shim.Success ([] byte (response.Message)} 7, get the Proposal attribute of the proposal object
(I) proposals for obtaining signatures
GetSignedProposal () (* pb.SignedProposal, error)
It is found that the Transaction or Query of the endorsement node from the client is a proposal, and the GetSignedProposal obtains the current proposal object, including the client's signature to the proposal. The content of the proposal includes proposals Header,Payload and Extension.
(2) get the Transient object
GetTransient () (map [string] [] byte, error)
Returns the property ChaincodeProposalPayload.TransientMap of the Payload of the proposal object
(3) obtain the transaction timestamp
GetTxTimestamp () (* timestamp.Timestamp, error)
Returns the proposal.Header.ChannelHeader.Timestamp of the proposal object
(4) get the Binding object
GetBinding () ([] byte, error)
Returns the combination of SignatureHeader.Nonce,SignatureHeader.Creator and ChannelHeader.Epoch in the proposal.Header of the proposal object.
8. Event settings
SetEvent (name string, payload [] byte) error
When the chain code is submitted, the client will be notified by event, and the content of the notification can be set through SetEvent. After the event is set, you need to modify it on the client side as well.
Func (t * SimpleChaincode) testEvent (stub shim.ChaincodeStubInterface, args [] string) pb.Response {tosend: = "Event send data is here!" Err: = stub.SetEvent ("evtsender" [] byte (tosend) if err! = nil {return shim.Error (err.Error ())} return shim.Success (nil)} IV. Chain code development example package mainimport ("fmt"strconv"github.com/hyperledger/fabric/core/chaincode/lib/cid"github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer") / / simple chain code implementation type SimpleChaincode struct {} / / Init initialization chain code func (t * SimpleChaincode) Init (stub shim.ChaincodeStubInterface) pb.Response {fmt.Println ("abac Init") err: = cid.AssertAttributeValue (stub) "abac.init", "true") if err! = nil {return shim.Error (err.Error ())} _, args: = stub.GetFunctionAndParameters () var A, B string / / Entities var Aval, Bval int / / Asset holdings if len (args)! = 4 {return shim.Error ("Incorrect number of arguments. Expecting 4 ")} / / initialize chain code A = args [0] Aval, err = strconv.Atoi (args [1]) if err! = nil {return shim.Error (" Expecting integer value for asset holding ")} B = args [2] Bval, err = strconv.Atoi (args [3]) if err! = nil {return shim.Error (" Expecting integer value for asset holding ")} fmt.Printf (" Aval =% d, Bval =% d\ n " Aval, Bval) / / write status to the book 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)} func (t * SimpleChaincode) Invoke (stub shim.ChaincodeStubInterface) pb.Response {fmt.Println ("abac Invoke") function, args: = stub.GetFunctionAndParameters () if function = = "invoke" {/ / transfer Transfer X amount from account A to account B return t.invoke (stub, args)} else if function = = "delete" {/ / delete account return t.delete (stub, args)} else if function = = "query" {return t.query (stub, args)} return shim.Error ("Invalid invoke function name. Expecting\ "invoke\"\ "delete\"\ "query\")} / / transfer the X amount from account A to account Bfunc (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] / / read status from the book / / 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)) / / execute transaction X, err = strconv.Atoi (args [2]) if err! = nil {return shim.Error ("Invalid transaction amount") Expecting an integer value ")} Aval = Aval-X Bval = Bval + X fmt.Printf (" Aval =% d, Bval =% d\ n ", Aval, Bval) / / write the status back to the book 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)} / / delete the account 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 chaincodefunc (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 main () {err: = shim.Start (new (SimpleChaincode)) if err! = nil {fmt.Printf (" Error starting Simple chaincode:% s ", err)}}
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.