Hyperledger fabric 1.0 代码解析 之 chaincode invoke or query
1.sdk或client发起chaincode invoke/query 请求,然后开始构建一个Proposal包体。 主要过程是通过getChaincodeSpec,设置为chainID和chaincodeName,然后通过CreateProposalFromCIS以及GetSignedProposal 继续打包,主要是得到一个transcation id(txid)加上各种包头等。
// getChaincodeSpec get chaincode spec from the cli cmd pramameters
func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
spec := &pb.ChaincodeSpec{}
if err := checkChaincodeCmdParams(cmd); err != nil {
return spec, err
}
// Build the spec
input := &pb.ChaincodeInput{}
if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
return spec, fmt.Errorf("Chaincode argument error: %s", err)
}
chaincodeLang = strings.ToUpper(chaincodeLang)
if pb.ChaincodeSpec_Type_value[chaincodeLang] == int32(pb.ChaincodeSpec_JAVA) {
return nil, fmt.Errorf("Java chaincode is work-in-progress and disabled")
}
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
}
2.通过EndorserClient发起请求,调用endorser(通常是某个或某些peer)的ProcessProposal函数,处理第一步得到的Proposal包体。从Hyperledger fabric 1.0 代码解析 之 ProcessProposal 最开始的流程图,可以看出,chaincode invoke和query首先需要调用simulateProposal和endorseProposal来完成整个流程。
3.simulateProposal过程中大多数情况chaincode不是system chaincode,这个时候需要首先调用GetChaincodeFromLSCC,从lscc获取到chaincode data。(所以这里其实会先有一个执行lscc chaincode的过程)
4.调用callChaincode,最终到chaincode的Invoke函数。该处Invoke函数一般为用户的Invoke函数,在此不作分析。
5.调用endoserProposal对结果进行背书。
6.如果是invoke操作,这里会涉及到之后与orderer通信,orderer创建block,再分发给peer的过程。
7.peer从orderer或者通过gossip协议从其他节点拿到区块。然后验证通过后写区块,更新数据库。该部分内容见hyperledger fabric 1.0 代码解析 之 Commit Block
// DeliverBlocks used to pull out blocks from the ordering service to
// distributed them across peers
func (b *blocksProviderImpl) DeliverBlocks() {
errorStatusCounter := 0
statusCounter := 0
defer b.client.Close()
for !b.isDone() {
msg, err := b.client.Recv()
if err != nil {
logger.Warningf("[%s] Receive error: %s", b.chainID, err.Error())
return
}
switch t := msg.Type.(type) {
case *orderer.DeliverResponse_Status:
//...
case *orderer.DeliverResponse_Block:
//从orderer节点获得了block区块
errorStatusCounter = 0
statusCounter = 0
seqNum := t.Block.Header.Number
marshaledBlock, err := proto.Marshal(t.Block)
if err != nil {
logger.Errorf("[%s] Error serializing block with sequence number %d, due to %s", b.chainID, seqNum, err)
continue
}
//
if err := b.mcs.VerifyBlock(gossipcommon.ChainID(b.chainID), seqNum, marshaledBlock); err != nil {
logger.Errorf("[%s] Error verifying block with sequnce number %d, due to %s", b.chainID, seqNum, err)
continue
}
numberOfPeers := len(b.gossip.PeersOfChannel(gossipcommon.ChainID(b.chainID)))
// Create payload with a block received
payload := createPayload(seqNum, marshaledBlock)
// Use payload to create gossip message
gossipMsg := createGossipMsg(b.chainID, payload)
logger.Debugf("[%s] Adding payload locally, buffer seqNum = [%d], peers number [%d]", b.chainID, seqNum, numberOfPeers)
// Add payload to local state payloads buffer
b.gossip.AddPayload(b.chainID, payload)
// Gossip messages with other nodes
logger.Debugf("[%s] Gossiping block [%d], peers number [%d]", b.chainID, seqNum, numberOfPeers)
b.gossip.Gossip(gossipMsg)
default:
logger.Warningf("[%s] Received unknown: ", b.chainID, t)
return
}
}
}
通过gossip从其他peer节点处获得区块
// validateMsg checks the signature of the message if exists,
// and also checks that the tag matches the message type
func (g *gossipServiceImpl) validateMsg(msg proto.ReceivedMessage) bool {
if err := msg.GetGossipMessage().IsTagLegal(); err != nil {
g.logger.Warning("Tag of", msg.GetGossipMessage(), "isn't legal:", err)
return false
}
//...
if msg.GetGossipMessage().IsDataMsg() {
blockMsg := msg.GetGossipMessage().GetDataMsg()
if blockMsg.Payload == nil {
g.logger.Warning("Empty block! Discarding it")
return false
}
// If we're configured to skip block validation, don't verify it
if g.conf.SkipBlockVerification {
return true
}
//拿到的是区块的消息
seqNum := blockMsg.Payload.SeqNum
rawBlock := blockMsg.Payload.Data
if err := g.mcs.VerifyBlock(msg.GetGossipMessage().Channel, seqNum, rawBlock); err != nil {
g.logger.Warning("Could not verify block", blockMsg.Payload.SeqNum, ":", err)
return false
}
}
// ...
return true
}