Rookie learning Fabric source learning - endorsement node chain interaction and container yards

Fabric 1.4 source code analysis and endorse the nodes interact container chain code

This document describes the endorsement of nodes and vessels interaction process chain code, the Endorser endorsement node sections, both deploy, upgrade or call chain code, the final will call ChaincodeSupport.LaunchInit () / Launch () and ChaincodeSupport.execute () method. Wherein the Launch () method of the container chain code starts, execute () method is called chain code.

1. Prepare

ChaincodeSupport.Launch () is first determined whether there is the version of the chain code according Handler peer side, it indicates the presence of the run. If present, the chain code method call lscc cs.Lifecycle.ChaincodeContainerInfo () obtains the start code data desired chain ChaincodeContainerInfo. Then call cs.Launcher.Launch () method to start chain code. Then determine whether to register a handler.

func (cs *ChaincodeSupport) Launch(chainID, chaincodeName, chaincodeVersion string, qe ledger.QueryExecutor) (*Handler, error) {
    cname := chaincodeName + ":" + chaincodeVersion
    if h := cs.HandlerRegistry.Handler(cname); h != nil {
        return h, nil
    }

    ccci, err := cs.Lifecycle.ChaincodeContainerInfo(chaincodeName, qe)
    if err != nil {
        // TODO: There has to be a better way to do this...
        if cs.UserRunsCC {
            chaincodeLogger.Error(
                "You are attempting to perform an action other than Deploy on Chaincode that is not ready and you are in developer mode. Did you forget to Deploy your chaincode?",
            )
        }

        return nil, errors.Wrapf(err, "[channel %s] failed to get chaincode container info for %s", chainID, cname)
    }

    if err := cs.Launcher.Launch(ccci); err != nil {
        return nil, errors.Wrapf(err, "[channel %s] could not launch chaincode %s", chainID, cname)
    }

    h := cs.HandlerRegistry.Handler(cname)
    if h == nil {
        return nil, errors.Wrapf(err, "[channel %s] claimed to start chaincode container for %s but could not find handler", chainID, cname)
    }

    return h, nil
}

type ChaincodeContainerInfo struct {
    Name        string
    Version     string
    Path        string
    Type        string
    CodePackage []byte

    // ContainerType is not a great name, but 'DOCKER' and 'SYSTEM' are the valid types
    ContainerType string
}

The Launch () method is mainly implemented in the core / chaincode / runtime_launcher.go Launch () method. In this method, calls r.Runtime.Start (ccci, codePackage) start chain code, in this method, the first calls c.LaunchConfig (cname, ccci.Type) to generate the required parameters to create a chain code LaunchConfig (Chain code type go / java / nodejs, and TLS configuration), and then the container is configured to start the chain code request StartContainerReq. Then call c.Processor.Process (ccci.ContainerType, scr) officially launched the chain code of the container. After the operation is complete, () inside the select-case statement to get the results blocked by Launch, and end the program.

func (r *RuntimeLauncher) Launch(ccci *ccprovider.ChaincodeContainerInfo) error {
    ...
    if !alreadyStarted {
        ...
        go func() {
            if err := r.Runtime.Start(ccci, codePackage); err != nil {
                startFailCh <- errors.WithMessage(err, "error starting container")
                return
            }
            exitCode, err := r.Runtime.Wait(ccci)
            if err != nil {
                launchState.Notify(errors.Wrap(err, "failed to wait on container exit"))
            }
            launchState.Notify(errors.Errorf("container exited with %d", exitCode))
        }()
    }

    var err error
    select {
    case <-launchState.Done():
        err = errors.WithMessage(launchState.Err(), "chaincode registration failed")
    case err = <-startFailCh:
        launchState.Notify(err)
        r.Metrics.LaunchFailures.With("chaincode", cname).Add(1)
    case <-timeoutCh:
        err = errors.Errorf("timeout expired while starting chaincode %s for transaction", cname)
        launchState.Notify(err)
        r.Metrics.LaunchTimeouts.With("chaincode", cname).Add(1)
    }

    ...
    return err
}

Was seen above, when the container starts chain code calls c.Processor.Process () method, which calls req.Do (v). There are three implementation are StartContainerReq, WaitContainerReq, StopContainerReq. When start calling StartContainerReq.

func (si StartContainerReq) Do(v VM) error {
    return v.Start(si.CCID, si.Args, si.Env, si.FilesToUpload, si.Builder)
}

2. Start the chain code system

Start system chain code (process mode), then v.Start (si.CCID, si.Args, si.Env, si.FilesToUpload, si.Builder) implementation is the core / container / inproccontroller / inproccontroller.go start ( )method.

func (vm *InprocVM) Start(ccid ccintf.CCID, args []string, env []string, filesToUpload map[string][]byte, builder container.Builder) error {
    path := ccid.GetName() // name=Name-Version
    // 获取已注册的inprocContainer模版
    ipctemplate := vm.registry.getType(path)
    ...
    instName := vm.GetVMName(ccid)
    // 构建chaincode实例ipc
    ipc, err := vm.getInstance(ipctemplate, instName, args, env)

    // 判断链码是否运行
    if ipc.running {
        return fmt.Errorf(fmt.Sprintf("chaincode running %s", path))
    }

    ipc.running = true

    go func() {
        defer func() {
            if r := recover(); r != nil {
                inprocLogger.Criticalf("caught panic from chaincode  %s", instName)
            }
        }()
        // 进程模式运行链码
        ipc.launchInProc(instName, args, env)
    }()

    return nil
}

In the start () method method, firstly obtains ccid the name, and then acquires the registered system chain code template ipctemplate The name, building system chain code example ipc stencil and args, env and other parameters, and then determines whether the operation of the system chain code, if not running, open the coroutine call launchInProc () method to start the system process chain code pattern.

It opened in two coroutine launchInProc () in a main coroutine execution shimStartInProc () method, two main coroutine execution HandleChaincodeStream () method. And two new channels, to facilitate the peer side and the communication side chain code.

func (ipc *inprocContainer) launchInProc(id string, args []string, env []string) error {
    if ipc.ChaincodeSupport == nil {
        inprocLogger.Panicf("Chaincode support is nil, most likely you forgot to set it immediately after calling inproccontroller.NewRegsitry()")
    }
    // 建立peer侧接收链码侧发送通道
    peerRcvCCSend := make(chan *pb.ChaincodeMessage)
    // 建立链码侧接收peer侧发送通道
    ccRcvPeerSend := make(chan *pb.ChaincodeMessage)
    var err error
    // 传递链码侧Handler对象运行状态的通道
    ccchan := make(chan struct{}, 1)
    // 传递peer侧Handler对象运行状态的通道
    ccsupportchan := make(chan struct{}, 1)
    shimStartInProc := _shimStartInProc // shadow to avoid race in test
    go func() {
        defer close(ccchan)
        inprocLogger.Debugf("chaincode started for %s", id)
        if args == nil {
            args = ipc.args
        }
        if env == nil {
            env = ipc.env
        }
        // 启动系统链码
        err := shimStartInProc(env, args, ipc.chaincode, ccRcvPeerSend, peerRcvCCSend)
        if err != nil {
            err = fmt.Errorf("chaincode-support ended with err: %s", err)
            _inprocLoggerErrorf("%s", err)
        }
        inprocLogger.Debugf("chaincode ended for %s with err: %s", id, err)
    }()

    // shadow function to avoid data race
    inprocLoggerErrorf := _inprocLoggerErrorf
    go func() {
        defer close(ccsupportchan)
        inprocStream := newInProcStream(peerRcvCCSend, ccRcvPeerSend)
        inprocLogger.Debugf("chaincode-support started for  %s", id)
        // 启动peer侧Handler处理句柄,创建消息循环,处理链码侧发送的消息
        err := ipc.ChaincodeSupport.HandleChaincodeStream(inprocStream)
        if err != nil {
            err = fmt.Errorf("chaincode ended with err: %s", err)
            inprocLoggerErrorf("%s", err)
        }
        inprocLogger.Debugf("chaincode-support ended for %s with err: %s", id, err)
    }()
    // 阻塞等待消息处理
    select {
    // 链码侧退出,关闭peer侧接收链码侧发送通道
    case <-ccchan:
        close(peerRcvCCSend)
        inprocLogger.Debugf("chaincode %s quit", id)
    // peer侧chaincode support退出
    case <-ccsupportchan:
        close(ccRcvPeerSend)
        inprocLogger.Debugf("chaincode support %s quit", id)
    case <-ipc.stopChan:
        close(ccRcvPeerSend)
        close(peerRcvCCSend)
        inprocLogger.Debugf("chaincode %s stopped", id)
    }
    return err
}
  • Side chain code:

shimStartInProc () method is executed on essentially StartInProc () method, first traversal environment variables, acquired CORE_CHAINCODE_ID_NAME, () creates a communication flow executed newInProcStream, essentially just two channels transmitting side and the receiving peer side chain code binding. Then perform chatWithPeer () method to interact with the peer side. chatWithPeer () first calls newChaincodeHandler () Create Handler side chain code, and then transmits a registration message first, and then open the message processing loop.

// Register on the stream
chaincodeLogger.Debugf("Registering.. sending %s", pb.ChaincodeMessage_REGISTER)
if err = handler.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil {
    return errors.WithMessage(err, "error sending chaincode REGISTER")
}
  • peer side:

The coroutine first newInProcStream () and creating a communication flow here just the opposite side chain code. Then call HandleChaincodeStream () method, first create peer side Handle, then call handler.ProcessStream (stream) stream communication processes (which also has a cycle).

Specific interaction process subsequent introduction.

3. Start the application chain code

When the chain starts the application code (Docker container mode), Start () interface of core / container / dockercontroller / dockercontroller.go Start () method.

In the Start () method, a first call GetVMNameForDocker method of generating image name networkId-peerid-name-version-Hash (networkId-peerid-name-version), generating vessel name (networkId-peerid-name-version call GetVMName () method ). Calling getClientFnc () Gets docker client, determine whether the current run chain code of the container, the container stops running currently running. Then call createContainer () to create the container, if the packet does not exist mirror, the mirror is constructed, and then create a chain code container. If you need to configure TLS, the call UploadToContainer () method to submit TLS certificate file. Then call StartContainer () officially launched the chain code of the container.

When the vessel chain code is started, performs shim.start () method. First, the flow will acquire a communication side to communicate with the peer. Then call chatWithPeer () method. Obtaining traffic flow method described here.

func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) {
    flag.StringVar(&peerAddress, "peer.address", "", "peer address")
    ... 
    // Establish connection with validating peer
    // 与peer建立连接
    clientConn, err := newPeerClientConnection()
    ...
    // 创建链码支持服务客户端
    chaincodeSupportClient := pb.NewChaincodeSupportClient(clientConn)
    ...
    // Establish stream with validating peer
    // 调用Register()接口获取通信流
    stream, err := chaincodeSupportClient.Register(context.Background())
    return stream, nil
}

When performing chaincodeSupportClient.Register () method performs peer side HandleChaincodeStream () method.

func (cs *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error {
    return cs.HandleChaincodeStream(stream)
}

4. endorsement node interaction and chain code

4.1 Preparation

Construction system in chain code chain code processes and applications, Use the peer side performs HandleChaincodeStream () method, the side chain code execution chatWithPeer () method, and to interact through a communication stream. Wherein the two methods is a method of message processing the handleMessage ()

  • Side chain code
switch handler.state {
case ready:
    err = handler.handleReady(msg, errc)
case established:
    err = handler.handleEstablished(msg, errc)
case created:
    err = handler.handleCreated(msg, errc)
default:
    err = errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}
  • peer side
switch h.state {
case Created:
    return h.handleMessageCreatedState(msg)
case Ready:
    return h.handleMessageReadyState(msg)
default:
    return errors.Errorf("handle message: invalid state %s for transaction %s", h.state, msg.Txid)
}

Next comes the message flow in accordance with

  1. Side chain code sends a REGISTER message
    • Firstly the basic configuration, and then establish a connection with gRPC Peer node.
    • Create a Handler, and change Handler status "Created".
    • Sends a REGISTER message to the peer node.
    • Waiting for peer node information returned
  2. peer side receives a REGISTER message
    • At this peer side Handler state "Created", call handleMessageCreatedState () inside HandleRegister () method.
    • peer side registration Handler, and send a message to REGISTERED side chain code
    • Handler Status Update peer side as "Established"
    • And calls notifyRegistry () method sends the message to the READY side chain code, and updates the status "Ready"
  3. Message receiving side chain code
    • When the receiving side chain code REGISTERED message, update state to state Handler "Established"
    • When the receiving side chain code READY message, update state to Handler state "Ready"

At this point, the container and the chain code peer node connected preparation operation is completed.

4.2 chain code execution

The main implementation is Execute () method. In endorsement node introduction, there are two message types: ChaincodeMessage_TRANSACTION / ChaincodeMessage_INIT. Corresponding code and instantiate the call chain chain code / upgrade chain code operation. At this time, the side chain code and the peer side Handler are in the Ready state. In this interactive process, essentially sending a message to peer side by side chain codes of chain code calling Init () / Invoke () method is completed, then the message is returned to the side chain code.

4.2.1 Examples of the chain code / upgrade chain code operation

Message type, the peer side is transmitted ChaincodeMessage_INIT. In ChaincodeSupport.execute () will call handler.execute () method.

func (h *Handler) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, msg *pb.ChaincodeMessage, timeout time.Duration) (*pb.ChaincodeMessage, error) {
    txParams.CollectionStore = h.getCollectionStore(msg.ChannelId)
    txParams.IsInitTransaction = (msg.Type == pb.ChaincodeMessage_INIT)
    // 创建交易上下文
    txctx, err := h.TXContexts.Create(txParams)
    if err != nil {
        return nil, err
    }
    // 删除交易上下文
    defer h.TXContexts.Delete(msg.ChannelId, msg.Txid)
    
    if err := h.setChaincodeProposal(txParams.SignedProp, txParams.Proposal, msg); err != nil {
        return nil, err
    }
    // 异步发送消息
    h.serialSendAsync(msg)

    var ccresp *pb.ChaincodeMessage
    // 等待链码侧响应
    select {
    case ccresp = <-txctx.ResponseNotifier:
        // response is sent to user or calling chaincode. ChaincodeMessage_ERROR
        // are typically treated as error
    case <-time.After(timeout):
        err = errors.New("timeout expired while executing transaction")
        ccName := cccid.Name + ":" + cccid.Version
        h.Metrics.ExecuteTimeouts.With(
            "chaincode", ccName,
        ).Add(1)
    }

    return ccresp, err
}

When the side chain code ChaincodeMessage_INIT type of message received calls handler.handleInit (msg, errc) method.

case pb.ChaincodeMessage_INIT:
        chaincodeLogger.Debugf("[%s] Received %s, initializing chaincode", shorttxid(msg.Txid), msg.Type)
        // Call the chaincode's Run function to initialize
        handler.handleInit(msg, errc)
        return nil
// handleInit handles request to initialize chaincode.
func (handler *Handler) handleInit(msg *pb.ChaincodeMessage, errc chan error) {
    go func() {
        var nextStateMsg *pb.ChaincodeMessage

        defer func() {
            // 协程结束时执行
            handler.triggerNextState(nextStateMsg, errc)
        }()
        ...
        // Get the function and args from Payload
        // 获取方法和参数
        input := &pb.ChaincodeInput{}
        unmarshalErr := proto.Unmarshal(msg.Payload, input)
    
        // Call chaincode's Run
        // Create the ChaincodeStub which the chaincode can use to callback
        stub := new(ChaincodeStub)
        err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
        // 执行链码的Init方法
        res := handler.cc.Init(stub)
        // Send COMPLETED message to chaincode support and change state
        nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
        chaincodeLogger.Debugf("[%s] Init succeeded. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
    }()
}

In handleInit (msg, errc) method, will be deserialized msg.Payload input chain code, which comprises Args. Chain code then calls the Init () method performs initialization chain code processes. And returns a result, the chain code event, the transaction id and packaged into channel id (Method triggerNextState () call serialSendAsync () to a peer) ChaincodeMessage_COMPLETED type sent to the peer side ChaincodeMessage

When the peer side receives the corresponding message. core / chaincode / handler.go handleMessageReadyState (). In this case it calls a Notify () method writes messages ResponseNotifier return channel response. Thereby completing chain code to instantiate / update process.

switch msg.Type {
case pb.ChaincodeMessage_COMPLETED, pb.ChaincodeMessage_ERROR:
    h.Notify(msg)

4.2.2 call chain code

Message type sent by peer side ChaincodeMessage_TRANSACTION. Similarly acquired ChaincodeMessage_TRANSACTION side chain code processes the message. It calls handler.handleTransaction (msg, errc) method for processing the message type. This type of message performs the above flow process and the like, but this time the Invoke method is called chain code. There will recall process state database interaction, and therefore sends a message to the peer side, the processing peer side state interact with the database, sending a message to the chain code side chain code-side process of transmitting After completion ChaincodeMessage_COMPLETED message to the peer side after the completion.

res := handler.cc.Invoke(stub)
  • Side chain code:
    when the chain code execution, the state needs to get messages from the database, e.g.
func (stub *ChaincodeStub) GetState(key string) ([]byte, error) {
    // Access public data by setting the collection to empty string
    collection := ""
    return stub.handler.handleGetState(collection, key, stub.ChannelId, stub.TxID)
}

CallPeerWithChaincodeMsg calls in handleGetState () method () method, and then call handler.sendReceive (msg, respChan) transmits the message to the message type ChaincodeMessage_GET_STATE peer side. Peer side returns a message waiting, then processed. ChaincodeMessage_COMPLETED message sent to the peer side after the process is completed.

  • peer side:
    when the peer side obtains the corresponding call message h.HandleTransaction (msg, h.HandleGetState) for processing. Finally, the message corresponding to the message type of encapsulated ChaincodeMessage_RESPONSE side chain code.
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid, ChannelId: msg.ChannelId}, nil

h.serialSendAsync(resp)

When the side chain code processing completion message to the sending peer ChaincodeMessage_COMPLETED side. peer side and then () method returns the message to the upper layer interfaces notify.

Other types of messages temporarily introduce, for details see the source code.

Among the above message interactive process, and the Peer will perform a side chain code operation that periodically sends messages to each other mutually ChaincodeMessage_KEEPALIVE to ensure each other online.

to sum up

This section describes the interaction process between the node and the endorsement chain code. This section describes the first chain code system and creating chain code process applications, and between the chain code describes how to establish a connection node and endorsements, how to send the message.

Guess you like

Origin www.cnblogs.com/jiliguo/p/12175400.html