Peer Channel Create
Towards Orderer -o 127.0.0.1:7050
$ peer channel create -c test1 -o 127.0.0.1:7050
var channelCmd = &cobra.Command{
Use: "channel",
Short: "Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.",
Long: "Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
common.InitCmd(cmd, args)
common.SetOrdererEnv(cmd, args)
},
}
create
InitCmdFactory needs to be done before executeCreate(cf). It has to set up the RPC with orderer.
func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
// the global chainID filled by the "-c" command
if channelID == common.UndefinedParamValue {
return errors.New("must supply channel ID")
}
// Parsing of the command line is done so silence cmd usage
cmd.SilenceUsage = true
var err error
if cf == nil {
cf, err = InitCmdFactory(EndorserNotRequired, PeerDeliverNotRequired, OrdererRequired)
if err != nil {
return err
}
}
return executeCreate(cf)
}
InitCmdFactory
Do some initialization. DeliverClient will be created from an orderClient.Deliver(), but BroadcastClient is not set here. Instead it will be instantiatized if need. Similar as DeliverClient, it will be created from orderClient.Broadcast(). Both DeliverC and BroadcastC will create a RPC/TCP connection towards Orderer. Thus, we will set up two underlying TCP connections…
// InitCmdFactory init the ChannelCmdFactory with clients to endorser and orderer according to params
func InitCmdFactory(isEndorserRequired, isPeerDeliverRequired, isOrdererRequired bool) (*ChannelCmdFactory, error) {
if isPeerDeliverRequired && isOrdererRequired {
// this is likely a bug during development caused by adding a new cmd
return nil, errors.New("ERROR - only a single deliver source is currently supported")
}
var err error
cf := &ChannelCmdFactory{}
cf.Signer, err = common.GetDefaultSignerFnc()
if err != nil {
return nil, errors.WithMessage(err, "error getting default signer")
}
cf.BroadcastFactory = func() (common.BroadcastClient, error) {
return common.GetBroadcastClientFnc()
}
// for join and list, we need the endorser as well
if isEndorserRequired {
// creating an EndorserClient with these empty parameters will create a
// connection using the values of "peer.address" and
// "peer.tls.rootcert.file"
cf.EndorserClient, err = common.GetEndorserClientFnc(common.UndefinedParamValue, common.UndefinedParamValue)
if err != nil {
return nil, errors.WithMessage(err, "error getting endorser client for channel")
}
}
// for fetching blocks from a peer
if isPeerDeliverRequired {
cf.DeliverClient, err = common.NewDeliverClientForPeer(channelID, cf.Signer, bestEffort)
if err != nil {
return nil, errors.WithMessage(err, "error getting deliver client for channel")
}
}
// for create and fetch, we need the orderer as well
if isOrdererRequired {
if len(strings.Split(common.OrderingEndpoint, ":")) != 2 {
return nil, errors.Errorf("ordering service endpoint %s is not valid or missing", common.OrderingEndpoint)
}
cf.DeliverClient, err = common.NewDeliverClientForOrderer(channelID, cf.Signer, bestEffort)
if err != nil {
return nil, err
}
}
logger.Infof("Endorser and orderer connections initialized")
return cf, nil
}
// GetBroadcastClient creates a simple instance of the BroadcastClient interface
func GetBroadcastClient() (BroadcastClient, error) {
oc, err := NewOrdererClientFromEnv()
if err != nil {
return nil, err
}
bc, err := oc.Broadcast()
if err != nil {
return nil, err
}
return &BroadcastGRPCClient{Client: bc}, nil
}
executeCreate
CreateChain --> GetGenesisBlock --> Persist block data to local disk
func executeCreate(cf *ChannelCmdFactory) error {
err := sendCreateChainTransaction(cf)
if err != nil {
return err
}
block, err := getGenesisBlock(cf)
if err != nil {
return err
}
b, err := proto.Marshal(block)
if err != nil {
return err
}
file := channelID + ".block"
if outputBlock != common.UndefinedParamValue {
file = outputBlock
}
err = ioutil.WriteFile(file, b, 0644)
if err != nil {
return err
}
return nil
}
Peer Channel fetch
Fetch a block of the channel specified, towards Orderer -o 127.0.0.1:7050 or if order address is not specified, it will fetch from the peer deliver service
peer channel fetch 0 -o 127.0.0.1:7050 -c test5
call stack
github.com/hyperledger/fabric/common/deliver.(*Handler).deliverBlocks at deliver.go:201
github.com/hyperledger/fabric/common/deliver.(*Handler).Handle at deliver.go:174
github.com/hyperledger/fabric/orderer/common/server.(*server).Deliver at server.go:209
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/orderer._AtomicBroadcast_Deliver_Handler at ab.pb.go:674
github.com/hyperledger/fabric/common/grpclogging.StreamServerInterceptor.func1 at server.go:130
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainStreamServer.func1.1.1 at chain.go:49
github.com/hyperledger/fabric/common/grpcmetrics.StreamServerInterceptor.func1 at interceptor.go:65
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainStreamServer.func1.1.1 at chain.go:49
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainStreamServer.func1 at chain.go:58
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).processStreamingRPC at server.go:1206
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).handleStream at server.go:1279
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1 at server.go:710
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1 at server.go:708
If tryting to fetch a block which doesn’t exist, it will hang up waiting for the block. It is actualy a cond variable, which can be waken up by cond.broadcast(), when the waited block is available, or the cusor/iterator is closed.
// Next moves the cursor to next block and returns true iff the iterator is not exhausted
func (itr *blocksItr) Next() (ledger.QueryResult, error) {
if itr.maxBlockNumAvailable < itr.blockNumToRetrieve {
itr.maxBlockNumAvailable = itr.waitForBlock(itr.blockNumToRetrieve)
}
...
Peer channel getinfo
Towards endorser
peer channel getinfo -c test1
func getinfo(cmd *cobra.Command, cf *ChannelCmdFactory) error {
//the global chainID filled by the "-c" command
if channelID == common.UndefinedParamValue {
return errors.New("Must supply channel ID")
}
// Parsing of the command line is done so silence cmd usage
cmd.SilenceUsage = true
var err error
if cf == nil {
cf, err = InitCmdFactory(EndorserRequired, PeerDeliverNotRequired, OrdererNotRequired)
if err != nil {
return err
}
}
client := &endorserClient{cf}
blockChainInfo, err := client.getBlockChainInfo()
Peer channel list
–> endorser
It will eventually invoke ChainCode ‘cscc’, “GetChannel” method… See the call stack below:
github.com/hyperledger/fabric/core/chaincode.(*Handler).serialSendAsync at handler.go:307
github.com/hyperledger/fabric/core/chaincode.(*Handler).Execute at handler.go:1207
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).execute at chaincode_support.go:272
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Invoke at chaincode_support.go:202
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Execute at chaincode_support.go:155
github.com/hyperledger/fabric/core/endorser.(*SupportImpl).Execute at support.go:126
github.com/hyperledger/fabric/core/endorser.(*Endorser).callChaincode at endorser.go:119
github.com/hyperledger/fabric/core/endorser.(*Endorser).SimulateProposal at endorser.go:187
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposalSuccessfullyOrError at endorser.go:397
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposal at endorser.go:340
github.com/hyperledger/fabric/core/handlers/auth/filter.(*expirationCheckFilter).ProcessProposal at expiration.go:61
github.com/hyperledger/fabric/core/handlers/auth/filter.(*filter).ProcessProposal at filter.go:32
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/peer._Endorser_ProcessProposal_Handler.func1 at peer.pb.go:107
github.com/hyperledger/fabric/internal/peer/node.unaryGrpcLimiter.func1 at grpc_limiters.go:51
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/common/grpclogging.UnaryServerInterceptor.func1 at server.go:92
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/common/grpcmetrics.UnaryServerInterceptor.func1 at interceptor.go:31
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1 at chain.go:34
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/peer._Endorser_ProcessProposal_Handler at peer.pb.go:109
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).processUnaryRPC at server.go:995
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).handleStream at server.go:1275
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1 at server.go:710
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1 at server.go:708
Msg will be asyncSent out, to system chain code for further processing.
// serialSendAsync serves the same purpose as serialSend (serialize msgs so gRPC will
// be happy). In addition, it is also asynchronous so send-remoterecv--localrecv loop
// can be nonblocking. Only errors need to be handled and these are handled by
// communication on supplied error channel. A typical use will be a non-blocking or
// nil channel
func (h *Handler) serialSendAsync(msg *pb.ChaincodeMessage) {
go func() {
if err := h.serialSend(msg); err != nil {
// provide an error response to the caller
resp := &pb.ChaincodeMessage{
Type: pb.ChaincodeMessage_ERROR,
Payload: []byte(err.Error()),
Txid: msg.Txid,
ChannelId: msg.ChannelId,
}
h.Notify(resp)
// surface send error to stream processing
h.errChan <- err
}
}()
}
System ChainCode
System Chain code is deployed as below:
// DeploySysCC is the hook for system chaincodes where system chaincodes are registered with the fabric.
// This call directly registers the chaincode with the chaincode handler and bypasses the other usercc constructs.
func DeploySysCC(sysCC SelfDescribingSysCC, chaincodeStreamHandler ChaincodeStreamHandler) {
sysccLogger.Infof("deploying system chaincode '%s'", sysCC.Name())
ccid := ChaincodeID(sysCC.Name())
done := chaincodeStreamHandler.LaunchInProc(ccid)
peerRcvCCSend := make(chan *pb.ChaincodeMessage)
ccRcvPeerSend := make(chan *pb.ChaincodeMessage)
go func() {
stream := newInProcStream(peerRcvCCSend, ccRcvPeerSend)
defer stream.CloseSend()
sysccLogger.Debugf("starting chaincode-support stream for %s", ccid)
err := chaincodeStreamHandler.HandleChaincodeStream(stream)
sysccLogger.Criticalf("shim stream ended with err: %v", err)
}()
go func(sysCC SelfDescribingSysCC) {
stream := newInProcStream(ccRcvPeerSend, peerRcvCCSend)
defer stream.CloseSend()
sysccLogger.Debugf("chaincode started for %s", ccid)
err := shim.StartInProc(ccid, stream, sysCC.Chaincode())
sysccLogger.Criticalf("system chaincode ended with err: %v", err)
}(sysCC)
<-done
}
- chaincodeStreamHandler takes peerRcvCCSend as receiver and ccRcvPeerSend as sender.
- shim takes ccRcvPeerSend as receiver and peerRcvCCSend as sender
- This makes the two switch their send/receive
shim.ChatWithPeer will process the message:
// chat stream for peer-chaincode interactions post connection
func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) 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
}
}
}
}
shim.Handler
It will invoke the system chain code
Call stack
github.com/hyperledger/fabric/core/peer.(*Peer).GetChannelsInfo at peer.go:412
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).getChannels at configure.go:297
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).InvokeNoShim at configure.go:190
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).Invoke at configure.go:132
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleTransaction at handler.go:209
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleTransaction-fm at handler.go:195
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleStubInteraction at handler.go:159
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleReady at handler.go:620
Peer channel join
–> endorser
peer channel join -b test5_0.block
call stack of shim handler
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).joinChain at configure.go:245
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).InvokeNoShim at configure.go:176
github.com/hyperledger/fabric/core/scc/cscc.(*PeerConfiger).Invoke at configure.go:132
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleTransaction at handler.go:209
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleTransaction-fm at handler.go:195
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleStubInteraction at handler.go:159
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-chaincode-go/shim.(*Handler).handleReady at handler.go:620
after joining channel, peer/endorser will start gossip service, if it gets elected as leader, it will start delivering for channel
StartDeliverForChannel() will try fetching block vis Orderer.Deliver service.
func (g *GossipService) onStatusChangeFactory(channelID string, committer blocksprovider.LedgerInfo) func(bool) {
return func(isLeader bool) {
if isLeader {
yield := func() {
g.lock.RLock()
le := g.leaderElection[channelID]
g.lock.RUnlock()
le.Yield()
}
logger.Info("Elected as a leader, starting delivery service for channel", channelID)
if err := g.deliveryService[channelID].StartDeliverForChannel(channelID, committer, yield); err != nil {
logger.Errorf("Delivery service is not able to start blocks delivery for chain, due to %+v", err)
}
} else {
logger.Info("Renounced leadership, stopping delivery service for channel", channelID)
if err := g.deliveryService[channelID].StopDeliverForChannel(channelID); err != nil {
logger.Errorf("Delivery service is not able to stop blocks delivery for chain, due to %+v", err)
}
}
}
}