您现在的位置是:主页 > news > 开通腾讯企业邮箱入口/seo关键词报价查询
开通腾讯企业邮箱入口/seo关键词报价查询
admin2025/5/15 1:56:11【news】
简介开通腾讯企业邮箱入口,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函数的实现
回到btcctl
的main
函数中来,我们来看一下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节的内容将放在下一篇博客中。