您现在的位置是:主页 > news > 开通腾讯企业邮箱入口/seo关键词报价查询

开通腾讯企业邮箱入口/seo关键词报价查询

admin2025/5/15 1:56:11news

简介开通腾讯企业邮箱入口,seo关键词报价查询,wordpress会员破解,页面设计工资有多少文章目录1. 写在前面2. 相关命令3. btcctl中的相关源码3.1 btcctl的main函数3.2 NewCmd的函数3.3 method的注册3.4 sendPostRequest函数的实现4. btcwallet中的相关代码4.1 作为服务端的btcwallet4.1.1 RawRequestAync函数相关4.1.2 Receive函数相关4.2 作为客户端的btcwallet1…

开通腾讯企业邮箱入口,seo关键词报价查询,wordpress会员破解,页面设计工资有多少文章目录1. 写在前面2. 相关命令3. btcctl中的相关源码3.1 btcctl的main函数3.2 NewCmd的函数3.3 method的注册3.4 sendPostRequest函数的实现4. btcwallet中的相关代码4.1 作为服务端的btcwallet4.1.1 RawRequestAync函数相关4.1.2 Receive函数相关4.2 作为客户端的btcwallet1…

文章目录

    • 1. 写在前面
    • 2. 相关命令
    • 3. btcctl中的相关源码
      • 3.1 btcctl的main函数
      • 3.2 NewCmd的函数
      • 3.3 method的注册
      • 3.4 sendPostRequest函数的实现
    • 4. btcwallet中的相关代码
      • 4.1 作为服务端的btcwallet
      • 4.1.1 RawRequestAync函数相关
      • 4.1.2 Receive函数相关
      • 4.2 作为客户端的btcwallet

1. 写在前面

我们从‘新区块的生成“开始我们的btcd源码之旅。

2. 相关命令

# start btcd
./btcd -u seafooler -P 123456 --simnet --miningaddr=SRgBmewVAfaVzqKMPHeFYwkiAGD8jKBWFz# start btcwallet
./btcwallet -u seafooler -P 123456 --simnet# generate a new block
./btcctl -u seafooler -P 123456 --wallet --simnet generate 1

3. btcctl中的相关源码

btcctl源码位于btcd整个源码树中,但其是个相对独立的模块,主要位于btcd/cmd/btcctl路径下。

3.1 btcctl的main函数

btcctl作为一个可执行程序,其必然存在main函数,在btcctl.go文件的L49.
main函数的前半部分主要是读取一些配置信息,我们为保证源码分析思路的清晰,先不去讲解这些配置信息,只在后面用到这些配置信息的时候再进行讲解。我们在进入到一个函数时,只会讲解跟当前分析最相关的那些代码。也就是说,我们可能会在不同的分析场景下多次讲解同一个函数。

// main[btcctl.go]
func main() {           // L49...cmd, err := btcjson.NewCmd(method, params...)           // L107...marshalledJSON, err := btcjson.MarshalCmd(1, cmd)           // L130...result, err := sendPostRequest(marshalledJSON, cfg)           // L138...
}

如上代码片段所示,method是从命令行参数中独取的, 利用NewCmd函数将其包装成一个cmd, 然后再利用MarshalCmd函数将其编码成Json格式,最后通过sendPostRequest函数将该命令发往btcwallet钱包进程。

3.2 NewCmd的函数

我们先来观察一下这个NewCmd函数。该函数定义在btcd/btcjson/cmdparser.go文件中。也就是说,btcctl是和btcd中的其他模块共享的该文件。

// main[btcctl.go] -> NewCmd[cmdparser.go]
func NewCmd(method string, args ...interface{}) (interface{}, error) {           // L511...rtp, ok := methodToConcreteType[method]           // L515info := methodToInfo[method]            // L516...
}

这一段代码涉及到反射的许多知识,相对来说是比较难理解的。其中涉及到的反射细节,我会单独再用一个章节来讲解。这里只需要知道method是存储在一个map字典中,从中查询到相应的变量。

3.3 method的注册

我们再来看一下generate这个method是何时以及如何注册到上述的map字典中的。

// main[btcctl.go] -> NewCmd[cmdparser.go] -> init[btcdextcmds.go]
func init() {...MustRegisterCmd("generate", (*GenerateCmd)(nil), flags)...
}
// main[btcctl.go] -> NewCmd[cmdparser.go] -> init[btcdextcmds.go] -> MustRegisterCmd[register.go]
func MustRegisterCmd(method string, cmd interface{}, flags UsageFlag) {if err := RegisterCmd(method, cmd, flags); err != nil {...
}
// main[btcctl.go] -> NewCmd[cmdparser.go] -> init[btcdextcmds.go] -> MustRegisterCmd[register.go] -> RegisterCmd[register.go] 
methodToConcreteType = make(map[string]reflect.Type)
methodToInfo         = make(map[string]methodInfo)func RegisterCmd(method string, cmd interface{}, flags UsageFlag) error {...rtp := reflect.TypeOf(cmd)...methodToConcreteType[method] = rtpmethodToInfo[method] = methodInfo{       maxParams:    numFields,       numReqParams: numFields - numOptFields,       numOptParams: numOptFields,       defaults:     defaults,       flags:        flags,}...
}

从上面三个代码片段可以看出,generate这个method是在btcdextcmds.go文件的初始化函数中被注册的。

3.4 sendPostRequest函数的实现

回到btcctlmain函数中来,我们来看一下sendPostRequest函数的实现细节。

// main[btcctl.go] ->  sendPostRequest[httpclient.go]
func sendPostRequest(marshalledJSON []byte, cfg *config) ([]byte, error) {...url := protocol + "://" + cfg.RPCServerbodyReader := bytes.NewReader(marshalledJSON)httpRequest, err := http.NewRequest("POST", url, bodyReader)...httpClient, err := newHTTPClient(cfg)...httpResponse, err := httpClient.Do(httpRequest)...    
}

这段代码是我们比较熟悉的,就是以POST方式调用url,并将Json数据作为附加数据发送出去。该url的地址和端口号在cfg.RPCServer中定义,其在main函数的loadConfig函数中的normalizeAddress函数中完成了初始化,如下所示:

// main[btcctl.go] -> loadConfig[config.go] -> normalizeAddress[config.go]
func normalizeAddress(addr string, useTestNet3, useSimNet, useWallet bool) string {...switch {case useSimNet:...defaultPort = "18554"...}...
}

18554端口是btcwallet进程监听的端口号,接下来我们进入btcwallet进行分析。

4. btcwallet中的相关代码

btcwallet具有两个职能:

  • 作为服务端,接收btcctl发过来的请求
  • 作为客户端,向btcd发送请求

同样地,btcwallet作为一个可以独立启动的进程,也必然是存在main函数的。而其main函数只是一个引子,其主要的功能代码都在walletMain函数中进行了实现。我们先来关注一下walletMain函数。

// walletMain[btcwallet.go]
func walletMain() error {               // L43...rpcs, legacyRPCServer, err := startRPCServers(loader)                // L77...go rpcClientConnectLoop(legacyRPCServer, loader)                    // L86...
}

其中L77行的代码,主要实现了接收btcctl请求的服务端功能;而L86行的代码,主要实现了向btcd发送请求的客户端功能。

4.1 作为服务端的btcwallet

我们首先来看一下作为服务端的btcwallet是如何接收btcctl的请求的。

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go]
func startRPCServers(walletLoader *wallet.Loader) (*grpc.Server, *legacyrpc.Server, error) {             // L105...legacyServer = legacyrpc.NewServer(&opts, walletLoader, listeners)               // L168...
}

功能主要是在L168行的NewServer函数中实现的。需要注意的是,尽管该函数名为"New…",但服务端的启动也在该函数中完成了。代码如下所示:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go]
func NewServer(opts *Options, walletLoader *wallet.Loader, listeners []net.Listener) *Server {      // L90...serveMux.Handle("/", throttledFn(opts.MaxPOSTClients,       // L117func(w http.ResponseWriter, r *http.Request) {...server.postClientRPC(w, r)                                  // L129...}))...
}

NewServer函数中L129行的postClientRPC函数用于接收btcctl的数据,并将数据进行处理后发往btcd,关键代码如下所示:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go]
func (s *Server) postClientRPC(w http.ResponseWriter, r *http.Request) {     // L566body := http.MaxBytesReader(w, r.Body, maxRequestSize)          // L567rpcRequest, err := ioutil.ReadAll(body)                             // L568...err = json.Unmarshal(rpcRequest, &req)                         // L581...switch req.Method {...default:       res, jsonErr = s.handlerClosure(&req)()}                    // L611}...
}

postClientRPC函数中的前半部分用于从网络中读取并解析出"req"请求,并通过L611行进行处理。下面我们来看一下handlerClosure函数的实现细节:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go]
func (s *Server) handlerClosure(request *btcjson.Request) lazyHandler{     // L274...return lazyApplyHandler(request, wallet, chainClient)                   // L285
}

再来看lazyApplyHandler的实现:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go]
func lazyApplyHandler(request *btcjson.Request, w *wallet.Wallet, chainClient chain.Interface) lazyHandler {     // L168handlerData, ok := rpcHandlers[request.Method]                          // L169if ok && handlerData.handlerWithChain != nil && w != nil && chainClient != nil {            // L170...}if ok && handlerData.handler != nil && w != nil {                   // L192...}return func() (interface{}, *btcjson.RPCError) {                        // L207...switch client := chainClient.(type) {case *chain.RPCClient:resp, err := client.RawRequest(request.Method, request.Params)......}}
}

L169首先从rpcHandlers字典中查找是否有注册的handler,若有就直接调用该handler,但generate命令并没有注册相应的handler (后面我们会举sendtoaddress的例子,其在rpcHandlers中进行了注册)。因此,lazyApplyHandler函数中的代码将运行至L207中。我们来看看RawRequest函数的实现:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go]
func (c *Client) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) {return c.RawRequestAsync(method, params).Receive()              // L77
}

RawRequest函数的实现很简单,只是做了一层函数的封装。但需要注意的是L77行末尾的Receive()调用。以下我们再分两个小节,分别讲解RawRequestAsync函数和Receive函数。

4.1.1 RawRequestAync函数相关

进一步查看RawRequestAsync函数:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> RawRequestAsync[rawrequest.go]
func (c *Client) RawRequestAsync(method string, params []json.RawMessage) FutureRawResult {...rawRequest := &btcjson.Request{       Jsonrpc: "1.0",       ID:      id,       Method:  method,       Params:  params,}marshalledJSON, err := json.Marshal(rawRequest)...responseChan := make(chan *response, 1)jReq := &jsonRequest{       id:             id,       method:         method,       cmd:            nil,       marshalledJSON: marshalledJSON,       responseChan:   responseChan,}c.sendRequest(jReq)             // L66return responseChan
}

RawRequestAsync函数首先将数据封装成jsonRequest请求,然后通过L66行的sendRequest函数将该请求发送出去。注意到RawRequestAsync函数返回的是FutureRawResult类型的变量,其会在4.1.2节中重点讲解。
sendRequest函数的实现细节如下所示:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> RawRequestAsync[rawrequest.go] -> sendRequest[infrastructure.go]
func (c *Client) sendRequest(jReq *jsonRequest) {...c.sendMessage(jReq.marshalledJSON)
}

我们再来看sendMessage函数:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> RawRequestAsync[rawrequest.go] -> sendRequest[infrastructure.go] -> sendMessage[infrastructure.go]
func (c *Client) sendMessage(marshalledJSON []byte) {select {case c.sendChan <- marshalledJSON:          // L481case <-c.disconnectChan():return}
}

sendMessage函数中将数据发送往sendChan管道,用于激活4.2节中wsOutHandler函数中阻塞的L449行代码。需要注意的是,RawRequestAync函数并没有直接将数据发送到btcd中,而是将数据发往sendChan管道。4.2.1.2节中的wsOutHandler函数才会真正将数据发往btcd

4.1.2 Receive函数相关

回到RawRequest函数中L77行末尾的Receive()调用。
准确来说,Receive函数已经不完全属于"服务端"的职能了,因为该函数用于接收从btcd返回的数据(如新生成的区块的hash值)。但其并没有直接与btcd打交道,而是通过FutureRawResult管道接收返回值。和btcd打交道的工作是由4.2.1.1小节的wsInHandler函数完成,该函数进一步激活了FutureRawResult管道。
前面已经提及RawRequestAsync函数返回的是FutureRawResult类型的变量。
下面我们关注一下FutureRawResult类型以及其Receive方法。

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> rawrequest.go
type FutureRawResult chan *response

注意到,FutureRawResult只是一个chan类型的重命名。

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> Receive[rawrequest.go]
func (r FutureRawResult) Receive() (json.RawMessage, error) {return receiveFuture(r)           // L21
}

该函数也只是对receiveFuture函数进行了一层封装,receiveFuture函数的实现如下所示:

// walletMain[btcwallet.go] -> startRPCServers[rpcserver.go] -> NewServe[server.go] -> postClientRPC[server.go] -> handlerClosure[server.go] -> lazyApplyHandler[methods.go] -> RawRequest[rawrequest.go] -> Receive[rawrequest.go] -> receiveFuture[infrastructure.go]
func receiveFuture(f chan *response) ([]byte, error) {r := <-f                // L797...
}

L797行形成了chan的阻塞,该阻塞将在4.2节的wsInHandler函数中被激活。

4.2 作为客户端的btcwallet

为避免这一篇博客过长,4.2节的内容将放在下一篇博客中。