1. 程式人生 > >區塊鏈教程Fabric1.0源代碼分析Peer peer chaincode命令及子命令實現

區塊鏈教程Fabric1.0源代碼分析Peer peer chaincode命令及子命令實現

重新 實現 nsa lse error pro all env mman

區塊鏈教程Fabric1.0源代碼分析Peer peer chaincode命令及子命令實現,2018年下半年,區塊鏈行業正逐漸褪去發展之初的浮躁、回歸理性,表面上看相關人才需求與身價似乎正在回落。但事實上,正是初期泡沫的漸退,讓人們更多的關註點放在了區塊鏈真正的技術之上。

Fabric 1.0源代碼筆記 之 Peer #peer chaincode命令及子命令實現

1、peer chaincode install子命令實現(安裝鏈碼)

1.0、peer chaincode install子命令概述

peer chaincode install,將鏈碼的源碼和環境封裝為一個鏈碼安裝打包文件,並傳輸到背書節點。

peer chaincode install支持如下兩種方式:

  • 指定代碼方式,peer chaincode install -n <鏈碼名稱> -v <鏈碼版本> -p <鏈碼路徑>
  • 基於鏈碼打包文件方式,peer chaincode install <鏈碼打包文件>
    技術分享圖片

    1.1、初始化Endorser客戶端

cf, err = InitCmdFactory(true, false)
//代碼在peer/chaincode/install.go

cf, err = InitCmdFactory(true, false)代碼如下:

func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
    var err error
    var endorserClient pb.EndorserClient
    if isEndorserRequired {
        //獲取Endorser客戶端
        endorserClient, err = common.GetEndorserClientFnc() //func GetEndorserClient() (pb.EndorserClient, error)
    }
    //獲取簽名
    signer, err := common.GetDefaultSignerFnc()
    var broadcastClient common.BroadcastClient
    if isOrdererRequired {
        //此處未用到,暫略
    }
    //構造ChaincodeCmdFactory
    return &ChaincodeCmdFactory{
        EndorserClient:  endorserClient,
        Signer:          signer,
        BroadcastClient: broadcastClient,
    }, nil
}
//代碼在peer/chaincode/common.go

1.2、構造ChaincodeDeploymentSpec消息(鏈碼信息及鏈碼文件打包)

if ccpackfile == "" { //指定代碼方式,重新構造構造ChaincodeDeploymentSpec消息
    ccpackmsg, err = genChaincodeDeploymentSpec(cmd, chaincodeName, chaincodeVersion)
} else { //基於鏈碼打包文件方式,直接讀取ChaincodeDeploymentSpec消息
    var cds *pb.ChaincodeDeploymentSpec
    ccpackmsg, cds, err = getPackageFromFile(ccpackfile)
}
//代碼在peer/chaincode/install.go

ccpackmsg, err = genChaincodeDeploymentSpec(cmd, chaincodeName, chaincodeVersion)代碼如下:

func genChaincodeDeploymentSpec(cmd *cobra.Command, chaincodeName, chaincodeVersion string) (*pb.ChaincodeDeploymentSpec, error) {
    //已經存在,直接報錯
    if existed, _ := ccprovider.ChaincodePackageExists(chaincodeName, chaincodeVersion); existed {
        return nil, fmt.Errorf("chaincode %s:%s already exists", chaincodeName, chaincodeVersion)
    }
    spec, err := getChaincodeSpec(cmd)
    cds, err := getChaincodeDeploymentSpec(spec, true)
    return cds, nil
}
//代碼在peer/chaincode/install.go

spec, err := getChaincodeSpec(cmd)代碼如下:

func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
    spec := &pb.ChaincodeSpec{}
    err := checkChaincodeCmdParams(cmd) //檢查參數合法性
    input := &pb.ChaincodeInput{}
    //flags.StringVarP(&chaincodeCtorJSON, "ctor", "c", "{}",ctor為鏈碼具體執行參數信息,默認為{}
    err := json.Unmarshal([]byte(chaincodeCtorJSON), &input)
    //flags.StringVarP(&chaincodeLang, "lang", "l", "golang",lang為鏈碼的編寫語言,默認為golang
    chaincodeLang = strings.ToUpper(chaincodeLang)
    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
}
//代碼在peer/chaincode/common.go

cds, err := getChaincodeDeploymentSpec(spec, true)代碼如下:

func getChaincodeDeploymentSpec(spec *pb.ChaincodeSpec, crtPkg bool) (*pb.ChaincodeDeploymentSpec, error) {
    var codePackageBytes []byte
    if chaincode.IsDevMode() == false && crtPkg {
        var err error
        err = checkSpec(spec) //檢查spec合法性
        codePackageBytes, err = container.GetChaincodePackageBytes(spec) //打包鏈碼文件及依賴文件
    }
    //構造ChaincodeDeploymentSpec
    chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
    return chaincodeDeploymentSpec, nil
//代碼在peer/chaincode/common.go

1.3、創建lscc Proposal並簽名

creator, err := cf.Signer.Serialize() //獲取簽名者
//按ChaincodeDeploymentSpec構造Proposal,即鏈碼ChaincodeDeploymentSpec消息作為參數傳遞給lscc系統鏈碼並調用
//調用createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy")
prop, _, err := utils.CreateInstallProposalFromCDS(msg, creator) 
var signedProp *pb.SignedProposal
signedProp, err = utils.GetSignedProposal(prop, cf.Signer) //簽名提案
//代碼在peer/chaincode/install.go

createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy")代碼如下:

func createProposalFromCDS(chainID string, msg proto.Message, creator []byte, policy []byte, escc []byte, vscc []byte, propType string) (*peer.Proposal, string, error) {
    var ccinp *peer.ChaincodeInput
    var b []byte
    var err error
    b, err = proto.Marshal(msg)
    switch propType {
    case "deploy":
        fallthrough
    case "upgrade": 
        cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
        ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b, policy, escc, vscc}}
    case "install": 
        ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
    }
    lsccSpec := &peer.ChaincodeInvocationSpec{ //構造lscc ChaincodeInvocationSpec
        ChaincodeSpec: &peer.ChaincodeSpec{
            Type:        peer.ChaincodeSpec_GOLANG,
            ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
            Input:       ccinp}}

    return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, lsccSpec, creator)
}
//代碼在protos/utils/proputils.go

1.4、提交並處理Proposal

proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
//代碼在peer/chaincode/install.go

2、peer chaincode instantiate子命令實現(實例化鏈碼)

2.0、peer chaincode instantiate概述

peer chaincode instantiate命令通過構造生命周期管理系統鏈碼(LSCC)的交易,將安裝過的鏈碼在指定通道上進行實例化調用。
在peer上創建容器啟動,並執行初始化操作。

2.1、初始化EndorserClient、Signer、及BroadcastClient

與2.1接近,附BroadcastClient初始化代碼如下:

cf, err = InitCmdFactory(true, true)
//代碼在peer/chaincode/instantiate.go
func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
    //初始化EndorserClient、Signer
    var broadcastClient common.BroadcastClient
    if isOrdererRequired {
        //flags.StringVarP(&orderingEndpoint, "orderer", "o", "", "Ordering service endpoint")
        //orderingEndpoint為orderer服務地址
        broadcastClient, err = common.GetBroadcastClientFnc(orderingEndpoint, tls, caFile)
    }
}
//代碼在peer/chaincode/common.go

2.2、構造ChaincodeDeploymentSpec消息

spec, err := getChaincodeSpec(cmd) //構造ChaincodeSpec,參考本文1.2
//構造ChaincodeDeploymentSpec,參考本文1.2,但無法打包鏈碼文件
cds, err := getChaincodeDeploymentSpec(spec, false)
//代碼在peer/chaincode/instantiate.go

2.3、創建lscc Proposal並簽名

creator, err := cf.Signer.Serialize() //獲取簽名者
//policyMarhsalled為flags.StringVarP(&policy, "policy", "P", common.UndefinedParamValue,即鏈碼關聯的背書策略
//即調用 createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy"),參考本文1.3
prop, _, err := utils.CreateDeployProposalFromCDS(chainID, cds, creator, policyMarhsalled, []byte(escc), []byte(vscc))
var signedProp *pb.SignedProposal
signedProp, err = utils.GetSignedProposal(prop, cf.Signer) //簽名提案
//代碼在peer/chaincode/instantiate.go

2.4、提交並處理Proposal、獲取Proposal響應並創建簽名交易Envelope

proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp)
if proposalResponse != nil {
    env, err := utils.CreateSignedTx(prop, cf.Signer, proposalResponse) //由Proposal創建簽名交易Envelope
    return env, nil
}
//代碼在peer/chaincode/instantiate.go

env, err := utils.CreateSignedTx(prop, cf.Signer, proposalResponse)代碼如下:

func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
    hdr, err := GetHeader(proposal.Header) //反序列化為common.Header
    pPayl, err := GetChaincodeProposalPayload(proposal.Payload) //反序列化為peer.ChaincodeProposalPayload
    signerBytes, err := signer.Serialize() //signer序列化
    shdr, err := GetSignatureHeader(hdr.SignatureHeader) //反序列化為common.SignatureHeader
    if bytes.Compare(signerBytes, shdr.Creator) != 0 { //Proposal創建者需與當前簽名者相同
        return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
    }
    hdrExt, err := GetChaincodeHeaderExtension(hdr) //Header.ChannelHeader反序列化為peer.ChaincodeHeaderExtension

    var a1 []byte
    for n, r := range resps {
        if n == 0 {
            a1 = r.Payload
            if r.Response.Status != 200 { //檢查Response.Status是否為200
                return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
            }
            continue
        }
        if bytes.Compare(a1, r.Payload) != 0 { //檢查所有ProposalResponse.Payload是否相同
            return nil, fmt.Errorf("ProposalResponsePayloads do not match")
        }
    }

    endorsements := make([]*peer.Endorsement, len(resps))
    for n, r := range resps {
        endorsements[n] = r.Endorsement
    }

    //如下為逐層構建common.Envelope
    cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
    propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl, hdrExt.PayloadVisibility)
    cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
    capBytes, err := GetBytesChaincodeActionPayload(cap)
    taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
    taas := make([]*peer.TransactionAction, 1)
    taas[0] = taa
    tx := &peer.Transaction{Actions: taas}
    txBytes, err := GetBytesTransaction(tx)
    payl := &common.Payload{Header: hdr, Data: txBytes}
    paylBytes, err := GetBytesPayload(payl)
    sig, err := signer.Sign(paylBytes)
    return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
}

//代碼在protos/utils/txutils.go

common.Envelope更詳細內容,參考:Fabric 1.0源代碼筆記 之 附錄-關鍵數據結構

2.5、向orderer廣播交易Envelope

err = cf.BroadcastClient.Send(env)
//代碼在peer/chaincode/instantiate.go

3、peer chaincode invoke子命令實現(調用鏈碼)

3.0、peer chaincode invoke概述

通過invoke命令可以調用運行中的鏈碼的方法。

3.1、初始化EndorserClient、Signer、及BroadcastClient

參考本文1.1和2.1。

cf, err = InitCmdFactory(true, true)
//代碼在peer/chaincode/invoke.go

3.2、構造ChaincodeInvocationSpec

spec, err := getChaincodeSpec(cmd) //構造ChaincodeSpec
invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec} //構造ChaincodeInvocationSpec
//代碼在peer/chaincode/common.go

3.3、創建Chaincode Proposal並簽名

creator, err := signer.Serialize()
var prop *pb.Proposal
prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_ENDORSER_TRANSACTION, cID, invocation, creator)
var signedProp *pb.SignedProposal
signedProp, err = putils.GetSignedProposal(prop, signer) //Proposal簽名
//代碼在peer/chaincode/common.go

3.4、提交並處理Proposal、獲取Proposal響應

var proposalResp *pb.ProposalResponse
proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp)
//代碼在peer/chaincode/common.go

3.5、創建簽名交易Envelope並向orderer廣播交易Envelope

if invoke {
    env, err := putils.CreateSignedTx(prop, signer, proposalResp) //創建簽名交易
    err = bc.Send(env) //廣播交易
}
//代碼在peer/chaincode/common.go

4、peer chaincode query子命令實現(查詢鏈碼)

與3、peer chaincode invoke子命令實現(調用鏈碼)基本相同,區別在於提交並處理Proposal後,不再創建交易以及廣播交易。

區塊鏈教程Fabric1.0源代碼分析Peer peer chaincode命令及子命令實現