Invoke流程

client端处理流程

peer/chaincode/invoke.go

func invokeCmd(cf *ChaincodeCmdFactory) *cobra.Command {
       chaincodeInvokeCmd = &cobra.Command{
              Use:       "invoke",
              Short:     fmt.Sprintf("Invoke the specified %s.", chainFuncName),
              Long:      fmt.Sprintf("Invoke the specified %s. It will try to commit the endorsed transaction to the network.", chainFuncName),
              ValidArgs: []string{"1"},
              RunE: func(cmd *cobra.Command, args []string) error {
                     return chaincodeInvoke(cmd, args, cf) //处理函数
              },
       }

       return chaincodeInvokeCmd
}

函数的调用流程为chaincodeInvoke chaincodeInvokeOrQuery ChaincodeInvokeOrQuery

func ChaincodeInvokeOrQuery(spec *pb.ChaincodeSpec, 
                            cID string, invoke bool, 
                            signer msp.SigningIdentity, 
                            endorserClient pb.EndorserClient, 
                            bc common.BroadcastClient) (*pb.ProposalResponse, error) {
	invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
	if customIDGenAlg != common.UndefinedParamValue {
		invocation.IdGenerationAlg = customIDGenAlg
	}

	creator, err := signer.Serialize()

	funcName := "invoke"
	if !invoke {
		funcName = "query"
	}

    // 返回一个给定序列化身份的方案
	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)

    // 处理提案,通过grpc发送给endorser处理
	var proposalResp *pb.ProposalResponse
	proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp)

	if invoke {
		if proposalResp != nil {
			// 组装一个签名的交易(这是一个信封信息)
			env, err := putils.CreateSignedTx(prop, signer, proposalResp)
			if err != nil {
				return proposalResp, fmt.Errorf("Could not assemble transaction, err %s", err)
			}

			// 广播发送信封给orderer
			if err = bc.Send(env); err != nil {
				return proposalResp, fmt.Errorf("Error sending transaction %s: %s", funcName, err)
			}
		}
	}

	return proposalResp, nil
}  

调用grpc发送给endorser处理

func (c *endorserClient) ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error) {
	out := new(ProposalResponse)
    // 调用grpc发送给endorser处理
	err := grpc.Invoke(ctx, "/protos.Endorser/ProcessProposal", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

endorser执行chaincode,基于读取和写入的key生成读写操作集,向客户端返回提案结果,包括读写操作集。

然后客户端把交易提交到order,交易内容包含来自提案结果的读写操作集

peer/common/orderclient.go中定义了广播给orderer的接口

type BroadcastClient interface {
       //Send data to orderer
       Send(env *cb.Envelope) error
       Close() error
}
func (s *broadcastClient) Send(env *cb.Envelope) error {
    //发送给orderer
	if err := s.client.Send(env); err != nil {
		return fmt.Errorf("Could not send :%s)", err)
	}
    //同步等待消息返回
	err := s.getAck()

	return err
}
type AtomicBroadcast_BroadcastClient interface {
	Send(*common.Envelope) error   //发送给order的接口
	Recv() (*BroadcastResponse, error)
	grpc.ClientStream
}
func (x *atomicBroadcastDeliverClient) Send(m *common.Envelope) error {
	return x.ClientStream.SendMsg(m)
}

发送到orderer后,orderer将排完序的交易封装到区块中去,见后面的流程

endorser处理流程

protos/peer/peer.pb.go中注册了处理函数

func _Endorser_ProcessProposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
       in := new(SignedProposal)
       if err := dec(in); err != nil {
              return nil, err
       }
       if interceptor == nil {
              return srv.(EndorserServer).ProcessProposal(ctx, in)
       }
       info := &grpc.UnaryServerInfo{
              Server:     srv,
              FullMethod: "/protos.Endorser/ProcessProposal",
       }
       handler := func(ctx context.Context, req interface{}) (interface{}, error) {
              return srv.(EndorserServer).ProcessProposal(ctx, req.(*SignedProposal))
       }
       return interceptor(ctx, in, info, handler)
}

endorser接收到invoke进行实际的处理,代码在core/endorser/endorser.go

func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
// 检查消息是否合法

//获取chainId

// chainID不为空
        //获取Ledger
		lgr := peer.GetLedger(chainID)
		if _, err := lgr.GetTransactionByID(txid)
		
		//检查ACL - 我们验证此提案符合链条的政策
		if err = e.checkACL(signedProp, chdr, shdr, hdrExt)

接下来的流程就是比较重要了

        // 模拟
        cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim)

	if chainID == "" {//没有chain的提案,如CSCC不需要认可
		pResp = &pb.ProposalResponse{Response: res}
	} else {
		// 认可并获得编组的ProposalResponse消息
		pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
    }

simulateProposal 方法处理模拟的主要动作包含

	//为链码检查ESCC和VSCC(验证用途的系统合约程序)
	err = e.checkEsccAndVscc(prop); 
    //执行提议获得模拟结果
    res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim)

callChaincode 最终调用core/chaincode/chaincodeexec.goExecuteChaincode,主要做了如下事情

    //创建链码调用规范
    spec, err = createCIS(cccid.Name, args)
    //执行提案,返回链码的原始响应
	res, ccevent, err = Execute(ctxt, cccid, spec)

再来详细看下执行提案的函数Execute

	//1、调用一个链上的Init方法
	cctyp := pb.ChaincodeMessage_INIT

    //2、如果不运行,启动将启动链码(如果运行返回为零),并且将等待链码的处理程序进入FSM就绪状态。
	cID, cMsg, err := theChaincodeSupport.Launch(ctxt, cccid, spec)

    //3、封装链码消息
	var ccMsg *pb.ChaincodeMessage
	ccMsg, err = createCCMessage(cctyp, cccid.TxID, cMsg)
    
    //4、执行事务并等待它完成,直到超时值。
    resp, err := theChaincodeSupport.Execute(ctxt, cccid, ccMsg, theChaincodeSupport.executetimeout)

执行提案的第2步Launch的处理流程

	//如果在map上,必须有一个连接的流
	chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName)
        // 如果尚未运行,launchAndWaitForRegister将启动容器。 如果找不到,使用targz创建图像
		cLang := cds.ChaincodeSpec.Type
		err = chaincodeSupport.launchAndWaitForRegister(context, cccid, cds, cLang, builder)

看下launchAndWaitForRegister的处理

	//如果不在map中,加入map
	notfy := chaincodeSupport.preLaunchSetup(canName)
    
    //创建容器,完成注册过程
	resp, err := container.VMCProcess(ipcCtxt, vmtype, sir)

执行提案的第4步Execute

	//完整性检查,此时链码必然运行
	chrte, ok := chaincodeSupport.chaincodeHasBeenLaunched(canName)

    //发送执行消息
 	if notfy, err = chrte.handler.sendExecuteMessage(ctxt, cccid.ChainID, msg, cccid.SignedProposal, cccid.Proposal);
 	
 	//如果sendExecuteMessage成功,需要删除事务上下文
 	chrte.handler.deleteTxContext(msg.Txid)

endorser处理完后,消息返回给客户端,消息再发送到orderer

order的处理流程(sbft)

sbft接收消息的流程在函数createConsensusStack

backend, err := backend.NewBackend(sbft.config.Peers, conn, persist)

NewBackend的最后会启动一个协程

	c.persistence = persist
	c.queue = make(chan Executable)
	go c.run()

协程的工作就是读队列

func (b *Backend) run() {
       for {
              //queue就是chan Executable,从队列中获取消息,否则阻塞
              e := <-b.queue
              //执行消息
              e.Execute(b)
       }
}
func (m *msgEvent) Execute(backend *Backend) {
       // 每个chainId对应一个处理函数
       backend.consensus[m.chainId].Receive(m.msg, m.src)
}

Receive工作函数接收到消息,进行处理

当一个primary节点p收到一个客户端请求,就会开始三段协议的执行过程

func (s *SBFT) Receive(m *Msg, src uint64) {
       log.Debugf("replica %d: received message from %d: %s", s.id, src, m)

       if h := m.GetHello(); h != nil {
              s.handleHello(h, src)
              return
       } else if req := m.GetRequest(); req != nil {
              //首先,primary节点会多播该请求到所有的其它服务节点
              s.handleRequest(req, src)
              return
       } else if vs := m.GetViewChange(); vs != nil {
              s.handleViewChange(vs, src)
              return
       } else if nv := m.GetNewView(); nv != nil {
              s.handleNewView(nv, src)
              return
       }

       if s.testBacklogMessage(m, src) {
              log.Debugf("replica %d: message for future seq, storing for later", s.id)
              s.recordBacklogMsg(m, src)
              return
       }

       //pbft算法使用三段提交协议
       s.handleQueueableMessage(m, src)
}

pbft算法使用三段提交协议,查看handleQueueableMessage函数

func (s *SBFT) handleQueueableMessage(m *Msg, src uint64) {
       if pp := m.GetPreprepare(); pp != nil {
              s.handlePreprepare(pp, src)
              return
       } else if p := m.GetPrepare(); p != nil {
              s.handlePrepare(p, src)
              return
       } else if c := m.GetCommit(); c != nil {
              s.handleCommit(c, src)
              return
       //如果在每次请求执行完成之后就生成证据,那么代价是及其昂贵的,因此,这些证据是阶段性的生成的,当一个请求的序列号被一些常数整除的时候(例如100),该请求执行完即可以生成证据。我们把这些请求执行完后的节点状态称为 checkpoint,把具有证据的 checkpoint 称为 stable checkpoint。
       } else if c := m.GetCheckpoint(); c != nil {
              s.handleCheckpoint(c, src)
              return
       }

三段提交协议:pre-prepare,prepare,commit。pre-prepare和 prepare 阶段被用来给在同一个 view
内发送的请求排序,即使提出请求顺序的 primary 阶段是恶意的;prepare 和 commit 阶段被用来保证跨 view
提交的请求的最终顺序。view的含义是以同一个节点为primary的为一个view。具体参考:

https://blockchain.iethpay.com/pbft.html

来查看invoke动作的消息处理函数

func (s *SBFT) handleRequest(req *Request, src uint64) {
    //是主节点才会执行发送给其他共识节点
	if s.isPrimary() && s.activeView {
                //通过验证后,批量发送给共识节点
                //当 primary 节点正在执行协议过程中的消息数量大于一个给定的数值时,primary 节点再接收到新的请求时,不会立马开始新的执行过程,它会把新收到的消息缓冲起来,稍后,缓冲区内的消息会作为一个 group 多播出去,有效的减小了 CPU 的负荷和消息的拥堵。
      			s.maybeSendNextBatch()
    }

maybeSendNextBatch函数的最后

        //进入三段提交协议的第一阶段,向view内的backup广播消息
        s.sendPreprepare(batch, committers)
	    s.broadcast(&Msg{&Msg_Preprepare{m}})

orderer处理流程(kafka)

kafka部分定义了两个接口

// Broadcast receives a stream of messages from a client for ordering
// Broadcast主要接收Peer的数据并在Orderer里面生成一系列数据块
func (s *server) Broadcast(srv ab.AtomicBroadcast_BroadcastServer) error {
       logger.Debugf("Starting new Broadcast handler")
       return s.bh.Handle(srv)
}

// Deliver sends a stream of blocks to a client after ordering
// peer从orderer获取数据的接口
func (s *server) Deliver(srv ab.AtomicBroadcast_DeliverServer) error {
       logger.Debugf("Starting new Deliver handler")
       return s.dh.Handle(srv)

当客户端推数据过来,orderer处理主函数

func (bh *handlerImpl) Handle(srv ab.AtomicBroadcast_BroadcastServer) error {

        // 推送消息到kafka
        if !support.Enqueue(msg) {...}
        // 最后向客户端返回一个成功
		err = srv.Send(&ab.BroadcastResponse{Status: cb.Status_SUCCESS})  
func (x *atomicBroadcastBroadcastServer) Send(m *BroadcastResponse) error {
       return x.ServerStream.SendMsg(m)
}

Enqueue接受消息并在接受时返回true,或者在关闭时返回false。由drainQueue goroutine调用,在调用广播处理程序的Handle()函数时生成。

func (ch *chainImpl) Enqueue(env *cb.Envelope) bool {
       if ch.halted {
              return false
       }

       logger.Debugf("[channel: %s] Enqueueing envelope...", ch.support.ChainID())
       // 发送KafkaMessage_Regular消息
       if err := ch.producer.Send(ch.partition, utils.MarshalOrPanic(newRegularMessage(utils.MarshalOrPanic(env)))); err != nil {
              return false
       }
       return !ch.halted // If ch.halted has been set to true while sending, we should return false
}

当收到一条新的KafkaMessage_Regular消息

			case *ab.KafkaMessage_Regular:
				env := new(cb.Envelope)
				if err := proto.Unmarshal(msg.GetRegular().Payload, env); err != nil { //解析数据
					continue
				}
				// 获取可生成下一个区块的数据
				batches, committers, ok := ch.support.BlockCutter().Ordered(env)
				if ok && len(batches) == 0 && timer == nil {
				    //重新设置下一个区块生成定时器
					timer = time.After(ch.batchTimeout)
					continue
				}
				// If !ok, batches == nil, so this will be skipped
				for i, batch := range batches {
					block := ch.support.CreateNextBlock(batch)  //创建一个新的数据块
					encodedLastOffsetPersisted = utils.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: in.Offset})
					//将新的区块数据写入到ledger中(RAM 或者FILE)
					ch.support.WriteBlock(block, committers[i], encodedLastOffsetPersisted)
					ch.lastCutBlock++
					logger.Debug("Batch filled, just cut block", ch.lastCutBlock)
				}
				if len(batches) > 0 {
					timer = nil
				}
			}
posted on 2017-04-15 14:13  云中大卫  阅读(523)  评论(0)    收藏  举报