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
}

浙公网安备 33010602011771号