peer执行命令

一. Client通过REST接口执行一条deploy命令

Rest服务器的创建:

	// Create and register the REST service if configured
	if viper.GetBool("rest.enabled") {
		go rest.StartOpenchainRESTServer(serverOpenchain, serverDevops)
	}

  

StartOpenchainRESTServer-》
       buildOpenchainRESTRouter()里面注册了ProcessChaincode!!!

 

http发送的post request内容如下:(里面的参数,在后面 REST API中都会有涉及,比如需要secureContext来判断是否已经登录)

{
   "jsonrpc": "2.0",
   "method": "deploy",
   "params": {
    "type": 1,
    "chaincodeID":{
        "name": "mycc"
    },
    "ctorMsg": {
         "function":"init",
         "args":[]
     },
    "secureContext": "jim"
  },
   "id": 1
 }

  

 

 

1、log

【INFO】【rest】REST processing chaincode request...
【INFO】【container】rootDirectory = /opt/gopath/src
【INFO】【rest】REST deploying chaincode...
【INFO】【container】rootDirectory = /opt/gopath/src
【INFO】【consensus/pbft】Replica 0 executing/committing request batch for view=0/seqNo=1 and digest 2SrAWwU2a2hikiTiWBF1Hq0cJDQM9WR9zc+d+hzpkpqOWC09yNl7RM7NO3/8yWgOP1h7/0MLyYVYLFISkkzjIw==
【INFO】【consensus/pbft】Creating batch with 1 requests
【INFO】【consensus/pbft】Replica 0 batch timer expired
【INFO】【rest】REST successfully deploy chaincode: {"jsonrpc":"2.0","result":{"status":"OK","message":"ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539"},"id":1}
【INFO】【rest】Successfully deployed chainCode: ee5b24a1f17c356dd5f6e37307922e39ddba12e5d2e203ed93401d7d05eb0dd194fb9070549c5dc31eb63f4e654dbd5a1d86cbb30c48e3ab1812590cd0f78539

 

 

 

2.根据REST processing chaincode request...可以定位到是如下函数,进行第一次的命令分发:

 

// ProcessChaincode implements JSON RPC 2.0 specification for chaincode deploy, invoke, and query.
func (s *ServerOpenchainREST) ProcessChaincode(rw web.ResponseWriter, req *web.Request) {
	restLogger.Info("REST processing chaincode request...")

	encoder := json.NewEncoder(rw)

	// Read in the incoming request payload
	reqBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		// Format the error appropriately and produce JSON RPC 2.0 response
		errObj := formatRPCError(InternalError.Code, InternalError.Message, "Internal JSON-RPC error when reading request body.")
		rw.WriteHeader(http.StatusInternalServerError)
		encoder.Encode(formatRPCResponse(errObj, nil))
		restLogger.Error("Internal JSON-RPC error when reading request body.")
		return
	}

	// Incoming request body may not be empty, client must supply request payload
	if string(reqBody) == "" {
		// Format the error appropriately and produce JSON RPC 2.0 response
		errObj := formatRPCError(InvalidRequest.Code, InvalidRequest.Message, "Client must supply a payload for chaincode requests.")
		rw.WriteHeader(http.StatusBadRequest)
		encoder.Encode(formatRPCResponse(errObj, nil))
		restLogger.Error("Client must supply a payload for chaincode requests.")
		return
	}

	// Payload must conform to the following structure
	var requestPayload rpcRequest

	// Decode the request payload as an rpcRequest structure.	There will be an
	// error here if the incoming JSON is invalid (e.g. missing brace or comma).
	err = json.Unmarshal(reqBody, &requestPayload)
	if err != nil {
		// Format the error appropriately and produce JSON RPC 2.0 response
		errObj := formatRPCError(ParseError.Code, ParseError.Message, fmt.Sprintf("Error unmarshalling chaincode request payload: %s", err))
		rw.WriteHeader(http.StatusBadRequest)
		encoder.Encode(formatRPCResponse(errObj, nil))
		restLogger.Errorf("Error unmarshalling chaincode request payload: %s", err)
		return
	}

	//
	// After parsing the request payload successfully, determine if the incoming
	// request payload contains an "id" member. If id is not included, the request
	// is assumed to be a notification. The Server MUST NOT reply to a Notification.
	// Notifications are not confirmable by definition, since they do not have a
	// Response object to be returned. As such, the Client would not be aware of
	// any errors (like e.g. "Invalid params","Internal error").
	//

	notification := false
	if requestPayload.ID == nil {
		notification = true
	}

	// Insure that JSON RPC version string is present and is exactly "2.0"
	if requestPayload.Jsonrpc == nil {
		// If the request is not a notification, produce a response.
		if !notification {
			// Format the error appropriately and produce JSON RPC 2.0 response
			errObj := formatRPCError(InvalidRequest.Code, InvalidRequest.Message, "Missing JSON RPC 2.0 version string.")
			rw.WriteHeader(http.StatusBadRequest)
			encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
		}
		restLogger.Error("Missing JSON RPC version string.")

		return
	} else if *(requestPayload.Jsonrpc) != "2.0" {
		// If the request is not a notification, produce a response.
		if !notification {
			// Format the error appropriately and produce JSON RPC 2.0 response
			errObj := formatRPCError(InvalidRequest.Code, InvalidRequest.Message, "Invalid JSON RPC 2.0 version string. Must be 2.0.")
			rw.WriteHeader(http.StatusBadRequest)
			encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
		}
		restLogger.Error("Invalid JSON RPC version string. Must be 2.0.")

		return
	}

	// Insure that the JSON method string is present and is either deploy, invoke or query
	if requestPayload.Method == nil {
		// If the request is not a notification, produce a response.
		if !notification {
			// Format the error appropriately and produce JSON RPC 2.0 response
			errObj := formatRPCError(InvalidRequest.Code, InvalidRequest.Message, "Missing JSON RPC 2.0 method string.")
			rw.WriteHeader(http.StatusBadRequest)
			encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
		}
		restLogger.Error("Missing JSON RPC 2.0 method string.")

		return
	} else if (*(requestPayload.Method) != "deploy") && (*(requestPayload.Method) != "invoke") && (*(requestPayload.Method) != "query") {
		// If the request is not a notification, produce a response.
		if !notification {
			// Format the error appropriately and produce JSON RPC 2.0 response
			errObj := formatRPCError(MethodNotFound.Code, MethodNotFound.Message, "Requested method does not exist.")
			rw.WriteHeader(http.StatusNotFound)
			encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
		}
		restLogger.Error("Requested method does not exist.")

		return
	}

	//
	// Confirm the requested chaincode method and execute accordingly
	//

	// Variable that will hold the execution result
	var result rpcResult

	if *(requestPayload.Method) == "deploy" {

		//
		// Chaincode deployment was requested
		//

		// Payload params field must contain a ChaincodeSpec message
		if requestPayload.Params == nil {
			// If the request is not a notification, produce a response.
			if !notification {
				// Format the error appropriately and produce JSON RPC 2.0 response
				errObj := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Client must supply ChaincodeSpec for chaincode deploy request.")
				rw.WriteHeader(http.StatusBadRequest)
				encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
			}
			restLogger.Error("Client must supply ChaincodeSpec for chaincode deploy request.")

			return
		}

		// Extract the ChaincodeSpec from the params field
		ccSpec := requestPayload.Params

		// Process the chaincode deployment request and record the result
		result = s.processChaincodeDeploy(ccSpec)
	} else {

		//
		// Chaincode invocation/query was reqested
		//

		// Because chaincode invocation/query requests require a ChaincodeInvocationSpec
		// message instead of a ChaincodeSpec message, we must initialize it here
		// before  proceeding.
		ccSpec := requestPayload.Params
		invokequeryPayload := &pb.ChaincodeInvocationSpec{ChaincodeSpec: ccSpec}

		// Payload params field must contain a ChaincodeSpec message
		if invokequeryPayload.ChaincodeSpec == nil {
			// If the request is not a notification, produce a response.
			if !notification {
				// Format the error appropriately and produce JSON RPC 2.0 response
				errObj := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Client must supply ChaincodeSpec for chaincode deploy request.")
				rw.WriteHeader(http.StatusBadRequest)
				encoder.Encode(formatRPCResponse(errObj, requestPayload.ID))
			}
			restLogger.Error("Client must supply ChaincodeSpec for chaincode invoke or query request.")

			return
		}

		// Process the chaincode invoke/query request and record the result
		result = s.processChaincodeInvokeOrQuery(*(requestPayload.Method), invokequeryPayload)
	}

	//
	// Generate correctly formatted JSON RPC 2.0 response payload
	//

	response := formatRPCResponse(result, requestPayload.ID)
	jsonResponse, _ := json.Marshal(response)

	// If the request is not a notification, produce a response.
	if !notification {
		rw.WriteHeader(http.StatusOK)
		rw.Write(jsonResponse)
	}

	// Make a clarification in the invoke response message, that the transaction has been successfully submitted but not completed
	if *(requestPayload.Method) == "invoke" {
		restLogger.Infof("REST successfully submitted invoke transaction: %s", string(jsonResponse))
	} else {
		restLogger.Infof("REST successfully %s chaincode: %s", *(requestPayload.Method), string(jsonResponse))
	}

	return
}

  

 

3. 然后进入如下函数进行具体的deploy操作

 

// processChaincodeDeploy triggers chaincode deploy and returns a result or an error
func (s *ServerOpenchainREST) processChaincodeDeploy(spec *pb.ChaincodeSpec) rpcResult {
	restLogger.Info("REST deploying chaincode...")

	// Check that the ChaincodeID is not nil.
	if spec.ChaincodeID == nil {
		// Format the error appropriately for further processing
		error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Payload must contain a ChaincodeID.")
		restLogger.Error("Payload must contain a ChaincodeID.")

		return error
	}

	// If the peer is running in development mode, confirm that the Chaincode name
	// is not left blank. If the peer is running in production mode, confirm that
	// the Chaincode path is not left blank. This is necessary as in development
	// mode, the chaincode is identified by name not by path during the deploy
	// process.
	if viper.GetString("chaincode.mode") == chaincode.DevModeUserRunsChaincode {
		//
		// Development mode -- check chaincode name
		//

		// Check that the Chaincode name is not blank.
		if spec.ChaincodeID.Name == "" {
			// Format the error appropriately for further processing
			error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Chaincode name may not be blank in development mode.")
			restLogger.Error("Chaincode name may not be blank in development mode.")

			return error
		}
	} else {
		//
		// Network mode -- check chaincode path
		//

		// Check that the Chaincode path is not left blank.
		if spec.ChaincodeID.Path == "" {
			// Format the error appropriately for further processing
			error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Chaincode path may not be blank.")
			restLogger.Error("Chaincode path may not be blank.")

			return error
		}
	}

	// Check that the CtorMsg is not left blank.
	if (spec.CtorMsg == nil) || (len(spec.CtorMsg.Args) == 0) {
		// Format the error appropriately for further processing
		error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Payload must contain a CtorMsg with a Chaincode function name.")
		restLogger.Error("Payload must contain a CtorMsg with a Chaincode function name.")

		return error
	}

	//
	// Check if security is enabled
	//

	if core.SecurityEnabled() {
		// User registrationID must be present inside request payload with security enabled
		chaincodeUsr := spec.SecureContext
		if chaincodeUsr == "" {
			// Format the error appropriately for further processing
			error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Must supply username for chaincode when security is enabled.")
			restLogger.Error("Must supply username for chaincode when security is enabled.")

			return error
		}

		// Retrieve the REST data storage path
		// Returns /var/hyperledger/production/client/
		localStore := getRESTFilePath()

		// Check if the user is logged in before sending transaction
         //需要查看是否已经登录了 if _, err := os.Stat(localStore + "loginToken_" + chaincodeUsr); err == nil { // No error returned, therefore token exists so user is already logged in restLogger.Infof("Local user '%s' is already logged in. Retrieving login token.", chaincodeUsr) // Read in the login token token, err := ioutil.ReadFile(localStore + "loginToken_" + chaincodeUsr) if err != nil { // Format the error appropriately for further processing error := formatRPCError(InternalError.Code, InternalError.Message, fmt.Sprintf("Fatal error when reading client login token: %s", err)) restLogger.Errorf("Fatal error when reading client login token: %s", err) return error } // Add the login token to the chaincodeSpec spec.SecureContext = string(token) // If privacy is enabled, mark chaincode as confidential if viper.GetBool("security.privacy") { spec.ConfidentialityLevel = pb.ConfidentialityLevel_CONFIDENTIAL } } else { // Check if the token is not there and fail if os.IsNotExist(err) { // Format the error appropriately for further processing error := formatRPCError(MissingRegistrationError.Code, MissingRegistrationError.Message, MissingRegistrationError.Data) restLogger.Error(MissingRegistrationError.Data) return error } // Unexpected error // Format the error appropriately for further processing error := formatRPCError(InternalError.Code, InternalError.Message, fmt.Sprintf("Unexpected fatal error when checking for client login token: %s", err)) restLogger.Errorf("Unexpected fatal error when checking for client login token: %s", err) return error } } // // Trigger the chaincode deployment through the devops service,这里进入了Devops的cli调用流程,后面的分析 参考cli命令的执行流程 // chaincodeDeploymentSpec, err := s.devops.Deploy(context.Background(), spec) // // Deployment failed // if err != nil { // Format the error appropriately for further processing error := formatRPCError(ChaincodeDeployError.Code, ChaincodeDeployError.Message, fmt.Sprintf("Error when deploying chaincode: %s", err)) restLogger.Errorf("Error when deploying chaincode: %s", err) return error } // // Deployment succeeded // // Clients will need the chaincode name in order to invoke or query it, record it chainID := chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name // // Output correctly formatted response // result := formatRPCOK(chainID) restLogger.Infof("Successfully deployed chainCode: %s", chainID) return result }

  

 

 

 

 

 

二. Client通过CLI执行一条deploy命令

1、Client通过CLI执行一条deploy命令

会走到如下函数:

 

这里传入的spec是如下数据:

   "params": {
    "type": 1,
    "chaincodeID":{
        "name": "mycc"
    },

 

// Deploy deploys the supplied chaincode image to the validators through a transaction
func (d *Devops) Deploy(ctx context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
	// get the deployment spec
	chaincodeDeploymentSpec, err := GetChaincodeBytes(ctx, spec)   //获取链式代码的spec(配置) 见下分析

	if err != nil {
		devopsLogger.Error(fmt.Sprintf("Error deploying chaincode spec: %v\n\n error: %s", spec, err))
		return nil, err
	}

	// Now create the Transactions message and send to Peer.

	transID := chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name

	var tx *pb.Transaction
	var sec crypto.Client

	if peer.SecurityEnabled() {//安全模式下
		if devopsLogger.IsEnabledFor(logging.DEBUG) {
			devopsLogger.Debugf("Initializing secure devops using context %s", spec.SecureContext)
		}
		sec, err = crypto.InitClient(spec.SecureContext, nil) 
		defer crypto.CloseClient(sec)

		// remove the security context since we are no longer need it down stream
		spec.SecureContext = ""

		if nil != err {
			return nil, err
		}

		if devopsLogger.IsEnabledFor(logging.DEBUG) {
			devopsLogger.Debugf("Creating secure transaction %s", transID)
		}
		tx, err = sec.NewChaincodeDeployTransaction(chaincodeDeploymentSpec, transID, spec.Attributes...)
		if nil != err {
			return nil, err
		}
	} else { //非安全模式
		if devopsLogger.IsEnabledFor(logging.DEBUG) {
			devopsLogger.Debugf("Creating deployment transaction (%s)", transID)
		}
          //生成Transaction 结构,用于后面的 tx, err = pb.NewChaincodeDeployTransaction(chaincodeDeploymentSpec, transID) //根据chaincode的配置和收到的transId
if err != nil { return nil, fmt.Errorf("Error deploying chaincode: %s ", err) } } if devopsLogger.IsEnabledFor(logging.DEBUG) { devopsLogger.Debugf("Sending deploy transaction (%s) to validator", tx.Txid) } resp := d.coord.ExecuteTransaction(tx) if resp.Status == pb.Response_FAILURE { err = fmt.Errorf(string(resp.Msg)) } return chaincodeDeploymentSpec, err }

  

 

下面两个代码片段是,spec转成chaincodeSpec格式的流程,暂时可以不用关心。

// GetChaincodeBytes get chaincode deployment spec given the chaincode spec
func GetChaincodeBytes(context context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
	mode := viper.GetString("chaincode.mode")
	var codePackageBytes []byte
	if mode != chaincode.DevModeUserRunsChaincode {
		devopsLogger.Debugf("Received build request for chaincode spec: %v", spec)
		var err error
		if err = CheckSpec(spec); err != nil {
			return nil, err
		}

		codePackageBytes, err = container.GetChaincodePackageBytes(spec)
		if err != nil {
			err = fmt.Errorf("Error getting chaincode package bytes: %s", err)
			devopsLogger.Error(fmt.Sprintf("%s", err))
			return nil, err
		}
	}
	chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
	return chaincodeDeploymentSpec, nil
}

  

 

 

// GetChaincodePackageBytes creates bytes for docker container generation using the supplied chaincode specification
func GetChaincodePackageBytes(spec *pb.ChaincodeSpec) ([]byte, error) {
	if spec == nil || spec.ChaincodeID == nil {
		return nil, fmt.Errorf("invalid chaincode spec")
	}

	inputbuf := bytes.NewBuffer(nil)
	gw := gzip.NewWriter(inputbuf)   //golang gzip 压缩
	tw := tar.NewWriter(gw)        //golang tar 压缩

	platform, err := platforms.Find(spec.Type)  //根据类型,golang 还是java 还是别的来获取platform处理
	
	if err != nil {
		return nil, err
	}

	err = platform.WritePackage(spec, tw)    //格式化转化 ,spec -> the container for the supplied chaincode specification
	if err != nil {
		return nil, err
	}

	tw.Close()
	gw.Close()

	if err != nil {
		return nil, err
	}

	chaincodePkgBytes := inputbuf.Bytes()

	return chaincodePkgBytes, nil
}

  

 

2.当生成tx结构后,调用如下函数进行 一次transaction!

resp := d.coord.ExecuteTransaction(tx)

 

d.coord 是PeerServer对象,ExecuteTransaction 是对应Engine的实现方法

 

本次请求被封装成交易Struct,该处理是在PeerServer中。

 

//ExecuteTransaction executes transactions decides to do execute in dev or prod mode
func (p *Impl) ExecuteTransaction(transaction *pb.Transaction) (response *pb.Response) {
	if p.isValidator {
		response = p.sendTransactionsToLocalEngine(transaction) //如果是VP节点,则发送给自己Engine处理
	} else {
		peerAddresses := p.discHelper.GetRandomNodes(1) //否则随机寻找一个vp节点发送过去
		response = p.SendTransactionsToPeer(peerAddresses[0], transaction)
	}
	return response
}

 

  

// sendTransactionsToLocalEngine send the transaction to the local engine (This Peer is a validator)
func (p *Impl) sendTransactionsToLocalEngine(transaction *pb.Transaction) *pb.Response {

	peerLogger.Debugf("Marshalling transaction %s to send to local engine", transaction.Type)
	data, err := proto.Marshal(transaction) //SerializeToOstream  ,序列化cli的请求, 参考 http://www.cnblogs.com/zhangqingping/archive/2012/10/28/2743274.html

	if err != nil {
		return &pb.Response{Status: pb.Response_FAILURE, Msg: []byte(fmt.Sprintf("Error sending transaction to local engine: %s", err))}
	}

	var response *pb.Response
	msg := &pb.Message{Type: pb.Message_CHAIN_TRANSACTION, Payload: data, Timestamp: util.CreateUtcTimestamp()}
	peerLogger.Debugf("Sending message %s with timestamp %v to local engine", msg.Type, msg.Timestamp)
	response = p.engine.ProcessTransactionMsg(msg, transaction)   //调用对应engine来处理该msg

	return response
}

 

下面是engine中采用的ProcessTransactionMsg函数:

// ProcessTransactionMsg processes a Message in context of a Transaction
func (eng *EngineImpl) ProcessTransactionMsg(msg *pb.Message, tx *pb.Transaction) (response *pb.Response) {
	//TODO: Do we always verify security, or can we supply a flag on the invoke ot this functions so to bypass check for locally generated transactions?
	if tx.Type == pb.Transaction_CHAINCODE_QUERY {   //根据收到的Transaction的类型来对应的处理消息
		if !engine.helper.valid {
			logger.Warning("Rejecting query because state is currently not valid")
			return &pb.Response{Status: pb.Response_FAILURE,
				Msg: []byte("Error: state may be inconsistent, cannot query")}
		}

		// The secHelper is set during creat ChaincodeSupport, so we don't need this step
		// cxt := context.WithValue(context.Background(), "security", secHelper)
		cxt := context.Background()
		//query will ignore events as these are not stored on ledger (and query can report
		//"event" data synchronously anyway)
		//query 查询时候调用的接口
		result, _, err := chaincode.Execute(cxt, chaincode.GetChain(chaincode.DefaultChain), tx)
		if err != nil {
			response = &pb.Response{Status: pb.Response_FAILURE,
				Msg: []byte(fmt.Sprintf("Error:%s", err))}
		} else {
			response = &pb.Response{Status: pb.Response_SUCCESS, Msg: result}
		}
	} else {
		// Chaincode Transaction,Txid 就是前面收到的transID:chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name
		response = &pb.Response{Status: pb.Response_SUCCESS, Msg: []byte(tx.Txid)}

		//TODO: Do we need to verify security, or can we supply a flag on the invoke ot this functions
		// If we fail to marshal or verify the tx, don't send it to consensus plugin
		if response.Status == pb.Response_FAILURE {
			return response
		}

		// Pass the message to the consenter (eg. PBFT) NOTE: Make sure engine has been initialized
		if eng.consenter == nil {
			return &pb.Response{Status: pb.Response_FAILURE, Msg: []byte("Engine not initialized")}
		}
		// TODO, do we want to put these requests into a queue? This will block until
		// the consenter gets around to handling the message, but it also provides some
		// natural feedback to the REST API to determine how long it takes to queue messages
		err := eng.consenter.RecvMsg(msg, eng.peerEndpoint.ID) //使用共识来处理msg
		if err != nil {
			response = &pb.Response{Status: pb.Response_FAILURE, Msg: []byte(err.Error())}
		}
	}
	return response
}		

 

 

上面eng.consenter是初始化engine的时候生成的 consenter 对象, 如果使用pbft则是 pbft.GetPlugin(stack)返回的对象。
下面是当时生成 consenter 对象 的代码:

 

// NewConsenter constructs a Consenter object if not already present
func NewConsenter(stack consensus.Stack) consensus.Consenter {

	plugin := strings.ToLower(viper.GetString("peer.validator.consensus.plugin"))
	if plugin == "pbft" {
		logger.Infof("Creating consensus plugin %s", plugin)
		return pbft.GetPlugin(stack)  //通过如下返回的对象func newObcBatch(id uint64, config *viper.Viper, stack consensus.Stack) *obcBatch
	}
	logger.Info("Creating default consensus plugin (noops)")
	return noops.GetNoops(stack)

}

 

 

 

所以上面的eng.consenter.RecvMsg会走到:(以下pbft的为猜测。。。如果是noops 其中这个err := eng.consenter.RecvMsg(msg, eng.peerEndpoint.ID) //使用共识来处理msg很好定位)

可能是生成pbft plugin的时候生成如下接口,然后通过event,是收到报文后到external.go文件中得RecvMsg去处理。。。

type obcBatch struct {
	obcGeneric
	externalEventReceiver
}


	external.go
	
	// RecvMsg is called by the stack when a new message is received
func (eer *externalEventReceiver) RecvMsg(ocMsg *pb.Message, senderHandle *pb.PeerID) error {
	eer.manager.Queue() <- batchMessageEvent{
		msg:    ocMsg,
		sender: senderHandle,
	}
	return nil
}

 

下面这个应该是每个共识收到报文后的处理,可考虑参考http://wutongtree.github.io/hyperledger/consensus

 

// allow the primary to send a batch when the timer expires
func (op *obcBatch) ProcessEvent(event events.Event) events.Event {
	logger.Debugf("Replica %d batch main thread looping", op.pbft.id)
	switch et := event.(type) {
	case batchMessageEvent:
		ocMsg := et
		return op.processMessage(ocMsg.msg, ocMsg.sender)
	case executedEvent:
		op.stack.Commit(nil, et.tag.([]byte))
	case committedEvent:
		logger.Debugf("Replica %d received committedEvent", op.pbft.id)
		return execDoneEvent{}
	case execDoneEvent:
		if res := op.pbft.ProcessEvent(event); res != nil {
			// This may trigger a view change, if so, process it, we will resubmit on new view
			return res
		}
		return op.resubmitOutstandingReqs()
	case batchTimerEvent:
		logger.Infof("Replica %d batch timer expired", op.pbft.id)
		if op.pbft.activeView && (len(op.batchStore) > 0) {
			return op.sendBatch()
		}
	case *Commit:
		// TODO, this is extremely hacky, but should go away when batch and core are merged
		res := op.pbft.ProcessEvent(event)
		op.startTimerIfOutstandingRequests()
		return res
	case viewChangedEvent:
		op.batchStore = nil
		// Outstanding reqs doesn't make sense for batch, as all the requests in a batch may be processed
		// in a different batch, but PBFT core can't see through the opaque structure to see this
		// so, on view change, clear it out
		op.pbft.outstandingReqBatches = make(map[string]*RequestBatch)

		logger.Debugf("Replica %d batch thread recognizing new view", op.pbft.id)
		if op.batchTimerActive {
			op.stopBatchTimer()
		}

		if op.pbft.skipInProgress {
			// If we're the new primary, but we're in state transfer, we can't trust ourself not to duplicate things
			op.reqStore.outstandingRequests.empty()
		}

		op.reqStore.pendingRequests.empty()
		for i := op.pbft.h + 1; i <= op.pbft.h+op.pbft.L; i++ {
			if i <= op.pbft.lastExec {
				continue
			}

			cert, ok := op.pbft.certStore[msgID{v: op.pbft.view, n: i}]
			if !ok || cert.prePrepare == nil {
				continue
			}

			if cert.prePrepare.BatchDigest == "" {
				// a null request
				continue
			}

			if cert.prePrepare.RequestBatch == nil {
				logger.Warningf("Replica %d found a non-null prePrepare with no request batch, ignoring")
				continue
			}

			op.reqStore.storePendings(cert.prePrepare.RequestBatch.GetBatch())
		}

		return op.resubmitOutstandingReqs()
	case stateUpdatedEvent:
		// When the state is updated, clear any outstanding requests, they may have been processed while we were gone
		op.reqStore = newRequestStore()
		return op.pbft.ProcessEvent(event)
	default:
		return op.pbft.ProcessEvent(event)
	}

	return nil
}

 

posted @ 2016-12-16 11:55  菜鸟程序monkey  阅读(1891)  评论(0)    收藏  举报