code version: fabric 2.0.1
register
chaincode need to register in peer before work, it start after stream is established, end with receiving REGISTERED meessage from peer. After registered, both chaincode and peer’s handler is ready to handle message.
Regardledss of chaincode actor as server or client, chaincode should register itself to peer. Take chaincode act as grpc client as example.
chaincode side
chaincode establish connection and new stream
when shim.start() is invoked in main(), chaincode will try to connect to peer, and then new stream which will be used to send and recv message.
// fabric-chaincode-go/shim/client.go
// NewRegisterClient ...
func NewRegisterClient(conn *grpc.ClientConn) (peerpb.ChaincodeSupport_RegisterClient, error) {
return peerpb.NewChaincodeSupportClient(conn).Register(context.Background())
}
// fabric-chaincode-go/shim/shim.go
//the non-mock user CC stream establishment func
func userChaincodeStreamGetter(name string) (ClientStream, error) {
if *peerAddress == "" {
return nil, errors.New("flag 'peer.address' must be set")
}
conf, err := internal.LoadConfig()
if err != nil {
return nil, err
}
conn, err := internal.NewClientConn(*peerAddress, conf.TLS, conf.KaOpts)
if err != nil {
return nil, err
}
return internal.NewRegisterClient(conn)
}
// Start chaincodes
func Start(cc Chaincode) error {
flag.Parse()
chaincodename := os.Getenv("CORE_CHAINCODE_ID_NAME")
if chaincodename == "" {
return errors.New("'CORE_CHAINCODE_ID_NAME' must be set")
}
//mock stream not set up ... get real stream
if streamGetter == nil {
streamGetter = userChaincodeStreamGetter
}
stream, err := streamGetter(chaincodename)
if err != nil {
return err
}
err = chaincodeAsClientChat(chaincodename, stream, cc)
return err
}
chaincode register
// chat stream for peer-chaincode interactions post connection
func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error {
// Create the shim handler responsible for all control logic
handler := newChaincodeHandler(stream, cc)
// Send the ChaincodeID during register.
chaincodeID := &peerpb.ChaincodeID{Name: chaincodename}
payload, err := proto.Marshal(chaincodeID)
if err != nil {
return fmt.Errorf("error marshalling chaincodeID during chaincode registration: %s", err)
}
// Register on the stream
if err = handler.serialSend(&peerpb.ChaincodeMessage{Type: peerpb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil {
return fmt.Errorf("error sending chaincode REGISTER: %s", err)
}
// holds return values from gRPC Recv below
type recvMsg struct {
msg *peerpb.ChaincodeMessage
err error
}
msgAvail := make(chan *recvMsg, 1)
errc := make(chan error)
receiveMessage := func() {
in, err := stream.Recv()
msgAvail <- &recvMsg{in, err}
}
go receiveMessage()
for {
select {
case rmsg := <-msgAvail:
switch {
case rmsg.err == io.EOF:
return errors.New("received EOF, ending chaincode stream")
case rmsg.err != nil:
err := fmt.Errorf("receive failed: %s", rmsg.err)
return err
case rmsg.msg == nil:
err := errors.New("received nil message, ending chaincode stream")
return err
default:
err := handler.handleMessage(rmsg.msg, errc)
if err != nil {
err = fmt.Errorf("error handling message: %s", err)
return err
}
go receiveMessage()
}
case sendErr := <-errc:
if sendErr != nil {
err := fmt.Errorf("error sending: %s", sendErr)
return err
}
}
}
}
peer side
peer handle new stream
Peer act as ( grpc) server which listen to chaincode’s connection after peer node start. After a new connection is accepted, ChaincodeSupport.HandleChaincodeStream() will be invoked when chaincode NewStream(). Peer will new a handler which handle message from chaincode.
// HandleChaincodeStream implements ccintf.HandleChaincodeStream for all vms to call with appropriate stream
func (cs *ChaincodeSupport) HandleChaincodeStream(stream ccintf.ChaincodeStream) error {
handler := &Handler{
Invoker: cs,
Keepalive: cs.Keepalive,
Registry: cs.HandlerRegistry,
ACLProvider: cs.ACLProvider,
TXContexts: NewTransactionContexts(),
ActiveTransactions: NewActiveTransactions(),
BuiltinSCCs: cs.BuiltinSCCs,
QueryResponseBuilder: &QueryResponseGenerator{MaxResultLimit: 100},
UUIDGenerator: UUIDGeneratorFunc(util.GenerateUUID),
LedgerGetter: cs.Peer,
DeployedCCInfoProvider: cs.DeployedCCInfoProvider,
AppConfig: cs.AppConfig,
Metrics: cs.HandlerMetrics,
TotalQueryLimit: cs.TotalQueryLimit,
}
return handler.ProcessStream(stream)
}
// Register the bidi stream entry point called by chaincode to register with the Peer.
func (cs *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error {
return cs.HandleChaincodeStream(stream)
}
ProcessStream
ProcessStream will run loop until stream is done(something happen in grpc conn or stream), It process message via handleMessage() when recv message,
After stream is done, it will degrester handler.
func (h *Handler) ProcessStream(stream ccintf.ChaincodeStream) error {
defer h.deregister()
h.mutex.Lock()
h.streamDoneChan = make(chan struct{})
h.mutex.Unlock()
defer close(h.streamDoneChan)
h.chatStream = stream
h.errChan = make(chan error, 1)
var keepaliveCh <-chan time.Time
if h.Keepalive != 0 {
ticker := time.NewTicker(h.Keepalive)
defer ticker.Stop()
keepaliveCh = ticker.C
}
// holds return values from gRPC Recv below
type recvMsg struct {
msg *pb.ChaincodeMessage
err error
}
msgAvail := make(chan *recvMsg, 1)
receiveMessage := func() {
in, err := h.chatStream.Recv()
msgAvail <- &recvMsg{in, err}
}
go receiveMessage()
for {
select {
case rmsg := <-msgAvail:
switch {
// Defer the deregistering of the this handler.
case rmsg.err == io.EOF:
chaincodeLogger.Debugf("received EOF, ending chaincode support stream: %s", rmsg.err)
return rmsg.err
case rmsg.err != nil:
err := errors.Wrap(rmsg.err, "receive from chaincode support stream failed")
chaincodeLogger.Debugf("%+v", err)
return err
case rmsg.msg == nil:
err := errors.New("received nil message, ending chaincode support stream")
chaincodeLogger.Debugf("%+v", err)
return err
default:
err := h.handleMessage(rmsg.msg)
if err != nil {
err = errors.WithMessage(err, "error handling message, ending stream")
chaincodeLogger.Errorf("[%s] %+v", shorttxid(rmsg.msg.Txid), err)
return err
}
go receiveMessage()
}
case sendErr := <-h.errChan:
err := errors.Wrapf(sendErr, "received error while sending message, ending chaincode support stream")
chaincodeLogger.Errorf("%s", err)
return err
case <-keepaliveCh:
// if no error message from serialSend, KEEPALIVE happy, and don't care about error
// (maybe it'll work later)
h.serialSendAsync(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_KEEPALIVE})
continue
}
}
}
handle meesage
As we say above, when stream is established, the state of peer and chaincode is “created”. Then chaincode will send REGISTER msg to peer, which will trigger peer to register handler, which will be active until stream is done. The state of peer will turn to “Established” after register handler and response REGISTERED msg to chaincode, and then turn to be “ready” after send READY msg to chaincode. At that time, both peer and chaincode is “ready”.
// handleMessage is called by ProcessStream to dispatch messages.
func (h *Handler) handleMessage(msg *pb.ChaincodeMessage) error {
chaincodeLogger.Debugf("[%s] Fabric side handling ChaincodeMessage of type: %s in state %s", shorttxid(msg.Txid), msg.Type, h.state)
if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
return nil
}
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)
}
}
func (h *Handler) handleMessageCreatedState(msg *pb.ChaincodeMessage) error {
switch msg.Type {
case pb.ChaincodeMessage_REGISTER:
h.HandleRegister(msg)
default:
return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in created state", msg.Txid, msg.Type)
}
return nil
}
// handleRegister is invoked when chaincode tries to register.
func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
chaincodeLogger.Debugf("Received %s in state %s", msg.Type, h.state)
chaincodeID := &pb.ChaincodeID{}
err := proto.Unmarshal(msg.Payload, chaincodeID)
if err != nil {
chaincodeLogger.Errorf("Error in received %s, could NOT unmarshal registration info: %s", pb.ChaincodeMessage_REGISTER, err)
return
}
// Now register with the chaincodeSupport
// Note: chaincodeID.Name is actually of the form name:version for older chaincodes, and
// of the form label:hash for newer chaincodes. Either way, it is the handle by which
// we track the chaincode's registration.
if chaincodeID.Name == "" {
h.notifyRegistry(errors.New("error in handling register chaincode, chaincodeID name is empty"))
return
}
h.chaincodeID = chaincodeID.Name
err = h.Registry.Register(h)
if err != nil {
h.notifyRegistry(err)
return
}
chaincodeLogger.Debugf("Got %s for chaincodeID = %s, sending back %s", pb.ChaincodeMessage_REGISTER, h.chaincodeID, pb.ChaincodeMessage_REGISTERED)
if err := h.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}); err != nil {
chaincodeLogger.Errorf("error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
h.notifyRegistry(err)
return
}
h.state = Established
chaincodeLogger.Debugf("Changed state to established for %s", h.chaincodeID)
// for dev mode this will also move to ready automatically
h.notifyRegistry(nil)
}
register handler
register just record handler in handlers.
// Register adds a chaincode handler to the registry.
// An error will be returned if a handler is already registered for the
// chaincode. An error will also be returned if the chaincode has not already
// been "launched", and unsolicited registration is not allowed.
func (r *HandlerRegistry) Register(h *Handler) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if r.handlers[h.chaincodeID] != nil {
chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", h.chaincodeID)
return errors.Errorf("duplicate chaincodeID: %s", h.chaincodeID)
}
// This chaincode was not launched by the peer but is attempting
// to register. Only allowed in development mode.
if r.launching[h.chaincodeID] == nil && !r.allowUnsolicitedRegistration {
return errors.Errorf("peer will not accept external chaincode connection %s (except in dev mode)", h.chaincodeID)
}
r.handlers[h.chaincodeID] = h
chaincodeLogger.Debugf("registered handler complete for chaincode %s", h.chaincodeID)
return nil
}