Fabric-ca client端初始化过程源码分析

本文从Fabric-ca源码入手,以newRegisterCommand()函数为例,简单分析client启动时的过程。Fabric-ca源码可以从github.com下载,本文以v1.4.6为例进行简单分析。

与server相似,本文也是从main.go开始:

// fabric-ca/cmd/fabric-ca-client/main.go

package main

import (
	...
)

// The fabric-ca client main
func main() {
	if err := command.RunMain(os.Args); err != nil {
		os.Exit(1)
	}
}

main()函数只是调用了package command中的RunMain()函数,所以接下来我们以RunMain()函数为起点来简析client端的启动过程,其中包括client端的初始化以及跟server端的初次交互:

// fabric-ca/cmd/fabric-ca-client/command/root.go  

package command

import "os"

// RunMain is the fabric-ca client main
func RunMain(args []string) error {
	// Save the os.Args
	saveOsArgs := os.Args
	os.Args = args

	// Execute the command
	cmdName := ""
	if len(args) > 1 {
		cmdName = args[1]
	}
	ccmd := NewCommand(cmdName)
	err := ccmd.Execute()

	// Restore original os.Args
	os.Args = saveOsArgs

	return err
}

不难看出,与server类似,都是先通过NewCommand()函数来添加命令,之后再通过Execute()来执行操作:

// fabric-ca/cmd/fabric-ca-client/command/clientcmd.go

// ClientCmd encapsulates cobra command that provides command line interface
// for the Fabric CA client and the configuration used by the Fabric CA client
type ClientCmd struct {
	// name of the sub command
	name string
	// rootCmd is the base command for the Hyerledger Fabric CA client
	rootCmd *cobra.Command
	// My viper instance
	myViper *viper.Viper
	// cfgFileName is the name of the configuration file
	cfgFileName string
	// homeDirectory is the location of the client's home directory
	homeDirectory string
	// clientCfg is the client's configuration
	clientCfg *lib.ClientConfig
	// cfgAttrs are the attributes specified via flags or env variables
	// and translated to Attributes field in registration
	cfgAttrs []string
	// cfgAttrReqs are the attribute requests specified via flags or env variables
	// and translated to the AttrReqs field in enrollment
	cfgAttrReqs []string
	// cfgCsrNames are the certificate signing request names specified via flags
	// or env variables
	cfgCsrNames []string
	// csrCommonName is the certificate signing request common name specified via the flag
	csrCommonName string
	// gencrl command argument values
	crlParams crlArgs
	// revoke command argument values
	revokeParams revokeArgs
	// profileMode is the profiling mode, cpu or mem or empty
	profileMode string
	// profileInst is the profiling instance object
	profileInst interface {
		Stop()
	}
	// Dynamically configuring identities
	dynamicIdentity identityArgs
	// Dynamically configuring affiliations
	dynamicAffiliation affiliationArgs
	// Set to log level
	logLevel string
}

// NewCommand returns new ClientCmd ready for running
func NewCommand(name string) *ClientCmd {
	c := &ClientCmd{
		myViper: viper.New(),
	}
	c.name = strings.ToLower(name)
	c.init()
	return c
}

...

// init initializes the ClientCmd instance
// It intializes the cobra root and sub commands and
// registers command flgs with viper
func (c *ClientCmd) init() {
	...

	c.rootCmd.AddCommand(c.newRegisterCommand(),
		newEnrollCmd(c).getCommand(),
		c.newReenrollCommand(),
		c.newRevokeCommand(),
		newGetCAInfoCmd(c).getCommand(),
		c.newGenCsrCommand(),
		c.newGenCRLCommand(),
		c.newIdentityCommand(),
		c.newAffiliationCommand(),
		createCertificateCommand(c))
	c.rootCmd.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Prints Fabric CA Client version",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Print(metadata.GetVersionInfo(cmdName))
		},
	})
	c.registerFlags()
	...
}

NewCommand()函数中,创建了一个*ClientCmd的对象,之后,调用该对象的init()方法。在init()方法中,首先实例化了*ClientCmd.rootCmd,其中会要执行checkAndEnableProfiling()来检查运行环境:

// checkAndEnableProfiling checks for the FABRIC_CA_CLIENT_PROFILE_MODE
// env variable, if it is set to "cpu", cpu profiling is enbled;
// if it is set to "heap", heap profiling is enabled
func (c *ClientCmd) checkAndEnableProfiling() error {
	...
}

// registerFlags registers command flags with viper
func (c *ClientCmd) registerFlags() {
	...
}

之后会调用AddCommand()函数来添加newRegisterCommand()newEnrollCmd(c).getCommand()newReenrollCommand()newRevokeCommand()newGetCAInfoCmd(c).getCommand()newGenCsrCommand()newGenCRLCommand()newIdentityCommand()newAffiliationCommand()createCertificateCommand()和获取client版本的命令。随后执行c.registerFlags()操作。registerFlags()函数中主要是注册一些命令行参数,这里就不细究了。

newRegisterCommand()

// fabric-ca/cmd/fabric-ca-client/command/register.go

func (c *ClientCmd) newRegisterCommand() *cobra.Command {
	...
}

// The client register main logic
func (c *ClientCmd) runRegister() error {
	...
}

newRegisterCommand()函数中,实例化了一个*cobra.Command命令对象,该对象中包含两个命令:ConfigInit()runRegister()ConfigInit()命令是为fabric-ca-client命令初始化一些配置,这里不做说明,而runRegister()中包含客户端注册的主要逻辑:首先实例化一个lib.Client结构体,之后导入client端的身份凭证client.LoadMyIdentity(),随后发起注册*Identity.Register()

// fabric-ca/lib/client.go

// Client is the fabric-ca client object
type Client struct {
	// The client's home directory
	HomeDir string `json:"homeDir,omitempty"`
	// The client's configuration
	Config *ClientConfig
	// Denotes if the client object is already initialized
	initialized bool
	// File and directory paths
	keyFile, certFile, idemixCredFile, idemixCredsDir, ipkFile, caCertsDir string
	// The crypto service provider (BCCSP)
	csp bccsp.BCCSP
	// HTTP client associated with this Fabric CA client
	httpClient *http.Client
	// Public key of Idemix issuer
	issuerPublicKey *idemix.IssuerPublicKey
}

// LoadMyIdentity loads the client's identity from disk
func (c *Client) LoadMyIdentity() (*Identity, error) {
	...
	return c.LoadIdentity(c.keyFile, c.certFile, c.idemixCredFile)
}

// LoadIdentity loads an identity from disk
func (c *Client) LoadIdentity(keyFile, certFile, idemixCredFile string) (*Identity, error) {
	...
	return c.NewIdentity(creds)
}

// NewIdentity creates a new identity
func (c *Client) NewIdentity(creds []credential.Credential) (*Identity, error) {
	...
	return NewIdentity(c, name, creds), nil
}

如上,在LoadMyIdentity()中会先调用Init()来初始化client,然后再调用LoadIdentity()函数,从硬盘中导入身份凭证。在LoadIdentity()中同样会调用Init()来初始化client,之后会调用NewCredential()接口来导入x509idemin格式的证书。证书读取完成后,调用NewIdentity()来创建新的身份认证。至此,身份导入过程就完成了,接下来就是注册了。

//fabric-ca/lib/identity.go

// Register registers a new identity
// @param req The registration request
func (i *Identity) Register(req *api.RegistrationRequest) (rr *api.RegistrationResponse, err error) {
	...
}

Register(),会将认证请求序列后,通过Post请求发送给服务端,并将服务端的应答返回给调用者。

// Post sends arbitrary request body (reqBody) to an endpoint.
// This adds an authorization header which contains the signature
// of this identity over the body and non-signature part of the authorization header.
// The return value is the body of the response.
func (i *Identity) Post(endpoint string, reqBody []byte, result interface{}, queryParam map[string]string) error {
	...
}

至此,注册命令就结束了。

posted @ 2020-03-13 15:57  落雷  阅读(512)  评论(0编辑  收藏  举报