Hyperledger fabric 1.0 代码解析 之 chaincode install
2017-08-01
1.sdk或client发起install chaincode请求,然后开始构建一个Proposal包体。 主要过程是通过createProposalFromCDS将chainId设置为”“,将ChaincodeID的Name指定为lscc,然后通过CreateProposalFromCIS以及GetSignedProposal 继续打包,主要是得到一个transcation id(txid)加上各种包头等。
// createProposalFromCDS returns a deploy or upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec
func createProposalFromCDS(chainID string, msg proto.Message, creator []byte, policy []byte, escc []byte, vscc []byte, propType string) (*peer.Proposal, string, error) {
//in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates.
var ccinp *peer.ChaincodeInput
var b []byte
var err error
if msg != nil {
b, err = proto.Marshal(msg)
if err != nil {
return nil, "", err
}
}
switch propType {
case "deploy":
fallthrough
case "upgrade":
/*暂时忽略相应情况内容,待之后相应部分分析*/
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}}
//...and get the proposal for it
return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, lsccSpec, creator)
}
2.通过EndorserClient发起请求,调用endorser(通常是某个或某些peer)的ProcessProposal函数,处理第一步得到的Proposal包体。从Hyperledger fabric 1.0 代码解析 之 ProcessProposal 最开始的流程图,可以看出,install chaincode的处理过程只需要调用simulateProposal,且因为lscc是systemCC,该次ProcessProposal最终只会调用一次ExecuteChaincode,调用的是lscc中install相关内容。
3.lscc端中的Invoke函数,处理install的情况,首先检查签名,及是否有install chaincode的相关权限。然后执行executeInstall来进行install chaincode的实际操作.
func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()
if len(args) < 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
function := string(args[0])
// Handle ACL:
// 1. get the signed proposal
sp, err := stub.GetSignedProposal()
if err != nil {
return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err))
}
switch function {
case INSTALL:
if len(args) < 2 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
// 2. 权限检查,查看是否有install chaincode的权限,检查签名证书是否有Admins权限
if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization for INSTALL on %s has been denied with error %s", args[1], err))
}
depSpec := args[1]
//executeInstall
err := lscc.executeInstall(stub, depSpec)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte("OK"))
case DEPLOY:
/*暂时忽略相应情况内容,待之后相应部分分析*/
case UPGRADE:
/*暂时忽略相应情况内容,待之后相应部分分析*/
case GETCCINFO, GETDEPSPEC, GETCCDATA:
/*暂时忽略相应情况内容,待之后相应部分分析*/
case GETCHAINCODES:
/*暂时忽略相应情况内容,待之后相应部分分析*/
case GETINSTALLEDCHAINCODES:
/*暂时忽略相应情况内容,待之后相应部分分析*/
return shim.Error(InvalidFunctionErr(function).Error())
}
4.executeInstall首先获取ccpack(一会用来打包chaincode),并对chaincodeName,chaincodeVersion进行检查,随后调用PutChaincodeToFS将chaincode写入文件系统。
// executeInstall implements the "install" Invoke transaction
func (lscc *LifeCycleSysCC) executeInstall(stub shim.ChaincodeStubInterface, ccbytes []byte) error {
ccpack, err := ccprovider.GetCCPackage(ccbytes)
if err != nil {
return err
}
cds := ccpack.GetDepSpec()
if cds == nil {
return fmt.Errorf("nil deployment spec from from the CC package")
}
if err = lscc.isValidChaincodeName(cds.ChaincodeSpec.ChaincodeId.Name); err != nil {
return err
}
if err = lscc.isValidChaincodeVersion(cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version); err != nil {
return err
}
//everything checks out..lets write the package to the FS
if err = ccpack.PutChaincodeToFS(); err != nil {
return fmt.Errorf("Error installing chaincode code %s:%s(%s)", cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, err)
}
return err
}
//PutChaincodeToFS - serializes chaincode to a package on the file system
func (ccpack *CDSPackage) PutChaincodeToFS() error {
/*各种为空的时候的错误处理,暂时忽略*/
ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
//return error if chaincode exists
path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
if _, err := os.Stat(path); err == nil {
return fmt.Errorf("chaincode %s exists", path)
}
//写入文件
if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil {
return err
}
return nil
}