以前說完了鏈碼的安裝過程,接下來講一下鏈碼的實例化過程好了,再而後是鏈碼的調用過程。其實這幾個過程內容已經很類似了,都是涉及到Proposal
,不過總體流程仍是要說一下的。 一樣,切入點仍然是fabric/peer/main.go
文件中的main()
方法:html
#這一句定義了關於經過Peer節點操做鏈碼的命令 mainCmd.AddCommand(chaincode.Cmd(nil))
而後是fabric/peer/chaincode/chaincode.go
文件中的Cmd()
方法,這裏則是具體的操做鏈碼的命令,其中就有對鏈碼進行實例化的命令:git
chaincodeCmd.AddCommand(instantiateCmd(cf))
最後調用到了fabric/peer/chaincode/instantiate.go
文件中的第57行的instantiate()
方法。也就是說,當咱們在Peer
節點執行如下命令時,最終會到這個方法:github
#以官方的實例化鏈碼的方法爲例 peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
接下來就看一下instantiate()
方法:json
#首先獲取要實例化的鏈碼的信息 spec, err := getChaincodeSpec(cmd) if err != nil { return nil, err }
getChaincodeSpec()
方法在peer/chaincode/common.go
文件中第69行:app
#將用戶實例化鏈碼所執行的命令做爲參數傳入進去 func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) { #定義一個ChaincodeSpec結構體 spec := &pb.ChaincodeSpec{} ====================ChaincodeSpec=========================== type ChaincodeSpec struct { #Type表示鏈碼的類型 有GOLANG,NODE,CAR,JAVA,UNDEFINED五種類型 Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"` #ChaincodeId也是一個結構體,定義了鏈碼的路徑信息,鏈碼的名稱以及版本信息 ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"` #ChaincodeInput結構體中定義鏈碼的功能以及函數參數信息 Input *ChaincodeInput `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` Timeout int32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } ====================ChaincodeSpec=========================== #對用戶輸入的命令進行檢查 if err := checkChaincodeCmdParams(cmd); err != nil { // unset usage silence because it's a command line usage error cmd.SilenceUsage = false return spec, err } #定義ChaincodeInput結構體,就是上面說過的那個 input := &pb.ChaincodeInput{} if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil { return spec, errors.Wrap(err, "chaincode argument error") } chaincodeLang = strings.ToUpper(chaincodeLang) #最後將建立的ChaincodeSpec結構體返回 spec = &pb.ChaincodeSpec{ Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]), ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion}, Input: input, } return spec, nil }
看一下checkChaincodeCmdParams()
方法作了哪些工做,在219行:ide
func checkChaincodeCmdParams(cmd *cobra.Command) error { #檢查用戶輸入的鏈碼名稱是否爲空字符串 if chaincodeName == common.UndefinedParamValue { return errors.Errorf("must supply value for %s name parameter", chainFuncName) } #調用的方法是否爲instantiate,install,upgrade,package其中的一個 if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName || cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName { if chaincodeVersion == common.UndefinedParamValue { return errors.Errorf("chaincode version is not provided for %s", cmd.Name()) } if escc != common.UndefinedParamValue { logger.Infof("Using escc %s", escc) } else { logger.Info("Using default escc") escc = "escc" } if vscc != common.UndefinedParamValue { logger.Infof("Using vscc %s", vscc) } else { logger.Info("Using default vscc") vscc = "vscc" } if policy != common.UndefinedParamValue { #獲取定義的策略,就好比 OR ('Org1MSP.member','Org2MSP.member')這條信息是否有誤 p, err := cauthdsl.FromString(policy) if err != nil { return errors.Errorf("invalid policy %s", policy) } policyMarshalled = putils.MarshalOrPanic(p) } #若是定義了配置文件,則從配置文件中讀取配置信息 if collectionsConfigFile != common.UndefinedParamValue { var err error collectionConfigBytes, err = getCollectionConfigFromFile(collectionsConfigFile) if err != nil { return errors.WithMessage(err, fmt.Sprintf("invalid collection configuration in file %s", collectionsConfigFile)) } } } #對用戶傳入的實例化參數好比:-c '{"Args":["init","a","100","b","200"]}' if chaincodeCtorJSON != "{}" { ... } return nil }
回到instantiate()
方法:函數
cds, err := getChaincodeDeploymentSpec(spec, false) if err != nil { return nil, fmt.Errorf("error getting chaincode code %s: %s", chaincodeName, err) }
獲取ChaincodeDeploymentSpec
這個結構體:code
type ChaincodeDeploymentSpec struct { #這個是以前獲取到的結構體 ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"` #鏈碼數據 CodePackage []byte `protobuf:"bytes,3,opt,name=code_package,json=codePackage,proto3" json:"code_package,omitempty"` #鏈碼的運行環境,有兩種,Docker容器或者直接在系統中運行 ExecEnv ChaincodeDeploymentSpec_ExecutionEnvironment `protobuf:"varint,4,opt,name=exec_env,json=execEnv,proto3,enum=protos.ChaincodeDeploymentSpec_ExecutionEnvironment" json:"exec_env,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` }
看一下如何獲取ChaincodeDeploymentSpec
結構體:orm
#定義了ChaincodeDeploymentSpec中的CodePackage var codePackageBytes []byte #判斷是否爲開發模式 if chaincode.IsDevMode() == false && crtPkg { var err error #若是不是則檢查鏈碼是否爲空,以及路徑是否正確 if err = checkSpec(spec); err != nil { return nil, err } #將鏈碼轉換爲Byte數據 codePackageBytes, err = container.GetChaincodePackageBytes(platformRegistry, spec) ... } #構造chaincodeDeploymentSpec並返回 chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes} return chaincodeDeploymentSpec, nil
回到instantiate()
方法:htm
#獲取一全個簽名者,須要對建立實例化鏈碼的Proposal進行簽名 creator, err := cf.Signer.Serialize() if err != nil { return nil, fmt.Errorf("error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err) } #要建立用於實例化鏈碼的Proposal了 prop, _, err := utils.CreateDeployProposalFromCDS(channelID, cds, creator, policyMarshalled, []byte(escc), []byte(vscc), collectionConfigBytes) if err != nil { return nil, fmt.Errorf("error creating proposal %s: %s", chainFuncName, err) }
看一下CreateDeployProposalFromCDS()
方法,看名字瞭解到是根據chaincodeDeploymentSpec
建立用於部署鏈碼的Proposal
:
func CreateDeployProposalFromCDS( #通道Id chainID string, cds *peer.ChaincodeDeploymentSpec, #簽名者 creator []byte, #具體的策略 policy []byte, #endorser system chaincode escc []byte, #Verification System ChainCode vscc []byte, collectionConfig []byte) (*peer.Proposal, string, error) { #下面的兩個方法調用的是同一個,只是傳入的參數不一樣,點進去 if collectionConfig == nil { return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc) } return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc, collectionConfig) }
該方法在538行,接下來的部分與客戶端安裝鏈碼所執行的流程基本是相同的,只有下面的一部分不一樣:
#對於實例化鏈碼來講,執行的是deploy與upgrade這兩部分,而安裝鏈碼則是install這部分,差別就在於ChaincodeInput結構體內的參數不一樣 case "deploy": fallthrough case "upgrade": cds, ok := msg.(*peer.ChaincodeDeploymentSpec) if !ok || cds == nil { return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal") } Args := [][]byte{[]byte(propType), []byte(chainID), b} Args = append(Args, args...) ccinp = &peer.ChaincodeInput{Args: Args} case "install": ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}} } // wrap the deployment in an invocation spec to lscc... lsccSpec := &peer.ChaincodeInvocationSpec{ ChaincodeSpec: &peer.ChaincodeSpec{ Type: peer.ChaincodeSpec_GOLANG, ChaincodeId: &peer.ChaincodeID{Name: "lscc"}, Input: ccinp, }, }
剩下的部分就再也不重複看了,能夠參考Fabric1.4源碼解析:客戶端安裝鏈碼這篇文章。 總的來講,整個流程共有如下幾部分:
ChaincodeDeploymentSpec
結構體.Proposal
進行簽名的Creator
。Proposal
,Proposal
的Header
定義爲ENDORSER_TRANSACTION
,表示是一個須要背書的交易。Creator
進行簽名操做。Peer
節點調用ProcessProposal()
方法進行處理,該方法的解析在這裏。這是一個很重要的方法。Peer
節點處理完成所返回的Response
消息後發送到Orderer
節點。Orderer
節點接收到消息後進行排序操做,若是是SOLO
模式則由Orderer
節點生成區塊,最後將區塊廣播至Peer
節點,Peer
節點接收到區塊消息後驗證有效性,最後更新帳本數據。原文出處:https://www.cnblogs.com/cbkj-xd/p/11149791.html