Fabric1.4源碼解析:鏈碼實例化過程

以前說完了鏈碼的安裝過程,接下來講一下鏈碼的實例化過程好了,再而後是鏈碼的調用過程。其實這幾個過程內容已經很類似了,都是涉及到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源碼解析:客戶端安裝鏈碼這篇文章。 總的來講,整個流程共有如下幾部分:

  1. 根據用戶執行實例化鏈碼的命令啓動全過程
  2. 獲取須要實例化鏈碼的基本信息
  3. 建立ChaincodeDeploymentSpec結構體.
  4. 獲取用於對Proposal進行簽名的Creator
  5. 建立ProposalProposalHeader定義爲ENDORSER_TRANSACTION,表示是一個須要背書的交易。
  6. 由以前獲取的Creator進行簽名操做。
  7. Peer節點調用ProcessProposal()方法進行處理,該方法的解析在這裏。這是一個很重要的方法。
  8. 接收到由Peer節點處理完成所返回的Response消息後發送到Orderer節點。
  9. Orderer節點接收到消息後進行排序操做,若是是SOLO模式則由Orderer節點生成區塊,最後將區塊廣播至Peer節點,
  10. Peer節點接收到區塊消息後驗證有效性,最後更新帳本數據。

最後附上參考連接:1.傳送門 2.傳送門

原文出處:https://www.cnblogs.com/cbkj-xd/p/11149791.html

相關文章
相關標籤/搜索