兄弟連區塊鏈技術培訓Fabric 1.0原始碼分析(34) Peer #peer chaincode命令及子命令實現

來源:互聯網
上載者:User

兄弟連區塊鏈技術培訓Fabric 1.0原始碼分析(34) Peer #peer chaincode命令及子命令實現

# 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用戶端


```go

cf, err = InitCmdFactory(true, false)

//代碼在peer/chaincode/install.go

```


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


```go

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訊息(鏈碼資訊及鏈碼檔案打包)


```go

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)代碼如下:


```go

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)代碼如下:


```go

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)代碼如下:


```go

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並簽名


```go

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")代碼如下:


```go

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


```go

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上建立容器啟動,並執行初始化操作。


![](peer_chaincode_instantiate.png)


### 2.1、初始化EndorserClient、Signer、及BroadcastClient


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


```go

cf, err = InitCmdFactory(true, true)

//代碼在peer/chaincode/instantiate.go

```


```go

func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {

    //初始化EndorserClient、Signer,略,參考1.1

    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

```


BroadcastClient更詳細內容,參考[Fabric 1.0原始碼筆記 之 Peer #BroadcastClient(Broadcast用戶端)](BroadcastClient.md)


### 2.2、構造ChaincodeDeploymentSpec訊息


```go

spec, err := getChaincodeSpec(cmd) //構造ChaincodeSpec,參考本文1.2

//構造ChaincodeDeploymentSpec,參考本文1.2,但無法打包鏈碼檔案

cds, err := getChaincodeDeploymentSpec(spec, false)

//代碼在peer/chaincode/instantiate.go

```


### 2.3、建立lscc Proposal並簽名


```go

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


```go

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)代碼如下:


```go

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原始碼筆記 之 附錄-關鍵資料結構(圖)](../annex/datastructure.md)


### 2.5、向orderer廣播交易Envelope


```go

err = cf.BroadcastClient.Send(env)

//代碼在peer/chaincode/instantiate.go

```


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


### 3.0、peer chaincode invoke概述


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

![](peer_chaincode_invoke(query).png)


### 3.1、初始化EndorserClient、Signer、及BroadcastClient


參考本文1.1和2.1。


```go

cf, err = InitCmdFactory(true, true)

//代碼在peer/chaincode/invoke.go

```


### 3.2、構造ChaincodeInvocationSpec


```go

spec, err := getChaincodeSpec(cmd) //構造ChaincodeSpec

invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec} //構造ChaincodeInvocationSpec

//代碼在peer/chaincode/common.go

```


### 3.3、建立Chaincode Proposal並簽名


```go

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響應


```go

var proposalResp *pb.ProposalResponse

proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp)

//代碼在peer/chaincode/common.go

```


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


```go

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後,不再建立交易以及廣播交易。

是科技發展太快,還是我們已經掉尾?7月7日起,每天兩小時跟清華微軟Google大牛團隊一起實戰區塊鏈。http://www.ydma.cn/open/course/16來來來,清華學霸尹成大哥帶你飛起來~~~

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.