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後,不再建立交易以及廣播交易。
相關文章
- Fabric 1.0原始碼分析(33) Peer #peer channel命令及子命令實現原始碼
- Fabric 1.0原始碼分析(32)Peer #peer根命令入口及載入子命令原始碼
- Fabric 1.0原始碼分析(32) Peer #peer node start命令實現原始碼
- Fabric 1.0原始碼分析(31) Peer原始碼
- Fabric 1.0原始碼分析(35)Peer #EndorserServer(Endorser服務端)原始碼Server服務端
- Fabric 1.0原始碼分析(36) Peer #EndorserClient(Endorser客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(37) Peer #DeliverClient(Deliver客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(38) Peer #BroadcastClient(Broadcast客戶端)原始碼ASTclient客戶端
- Fabric 1.0原始碼分析(3)Chaincode(鏈碼)原始碼AI
- Fabric 1.0原始碼分析(5)Chaincode(鏈碼)體系總結原始碼AI
- Fabric1.4原始碼解析:Peer節點加入通道原始碼
- Fabric 1.0原始碼分析(4)Chaincode(鏈碼)#platforms(鏈碼語言平臺)原始碼AIPlatform
- 以太坊原始碼分析(31)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(32)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(46)p2p-peer.go原始碼分析原始碼Go
- Fabric 1.0原始碼分析(25) Orderer原始碼
- Fabric 1.0原始碼分析(40) Proposal(提案)原始碼
- Jaeger tchannel-go原始碼閱讀——PeerGo原始碼
- Fabric 1.0原始碼分析(18) Ledger(賬本)原始碼
- Fabric 1.0原始碼分析(43) Tx(Transaction 交易)原始碼
- Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計原始碼Go
- Fabric 1.0原始碼分析(42)scc(系統鏈碼)原始碼
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Fabric 1.0原始碼分析(26)Orderer #ledger(Orderer Ledger)原始碼
- Fabric 1.0原始碼分析(39) policy(背書策略)原始碼
- Fabric 1.0原始碼分析(15)gossip(流言演算法)原始碼Go演算法
- Fabric 1.0原始碼分析(23)LevelDB(KV資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(44)Tx #RWSet(讀寫集)原始碼
- Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)原始碼
- Fabric 1.0原始碼分析(10)consenter(共識外掛)原始碼
- Fabric 1.0原始碼分析(29) Orderer #multichain(多鏈支援包)原始碼AI
- Fabric 1.0原始碼分析(41)putils(protos/utils工具包)原始碼
- Fabric 1.0原始碼分析(45)gRPC(Fabric中註冊的gRPC Service)原始碼RPC
- Fabric 1.0原始碼分析(2) blockfile(區塊檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(19) Ledger #statedb(狀態資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(21)Ledger #historydb(歷史資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(22)Ledger #blkstorage(block檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(42)scc(系統鏈碼) #cscc(通道相關)原始碼