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 @   落雷  阅读(516)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示