在這裡,我們暫時先拋開bitcoin**,僅僅來談rpc,提到rpc大家肯定首先會想到遠端過程服務呼叫,既然是呼叫,那就肯定存在乙個client端和乙個server端,clent端與server端通過rpc這個黑盒通過http請求進行互動,那麼就有乙個問題,我自定義的json格式的字串(這裡拿json來進行舉例)是無法在網路上流通的,所以,必然會涉及到乙個json的序列化與反序列化的過程。綜上所述,大致流程如下:
rpc命令的入口函式是從bitcoin-abc/src/rpc/register.h
出發的,根據功能模組的不同,分了如下函式用來註冊rpc命令:
class
crpctable;
//區塊鏈rpc命令註冊
void
registerblockchainrpccommands
(crpctable &tablerpc)
;//p2p網路rpc命令註冊
void
registernetrpccommands
(crpctable &tablerpc)
;//其他工具rpc命令註冊
void
registermiscrpccommands
(crpctable &tablerpc)
;//挖礦rpc命令註冊
void
registerminingrpccommands
(crpctable &tablerpc)
;//交易prc命令註冊
void
registerrawtransactionrpccommands
(crpctable &tablerpc)
;//bch特有的rpc命令註冊
void
registerabcrpccommands
(crpctable &tablerpc)
;
首先來看crpctable
這個類,它是乙個排程表變數,專門用來儲存rpc命令,它有乙個很重要的方法,如下:
bool
(const
std::string &name, const crpccommand *pcmd)
;
其他模組的rpc命令通過這個函式不斷追加到rpctable中。有兩個引數,乙個是rpc命令的名字name,另乙個是指向rpc command的乙個指標。
接下來我們看一下crpccommand
這個類,它主要有如下定義:
std::string category;
std::string name;
rpcfn_type actor;
bool oksafemode;
std::vector
<:string> argnames;
它表示了當我需要新增加乙個rpc命令時,我這個新增加的命令需要描述如上所述的資訊。
下面,我們一起探索一下,各個模組都是怎麼註冊自己的rpc命令的,先看blockchain,具體如下:
static
const crpccommand commands = },
},},
},},
},};
篇幅有限,只列舉其中幾個。區塊鏈rpc命令在常量陣列commands中儲存,name就是我們在client中可以使用的rpc命令,那麼他是如何註冊到crpctable中的呢?
void
registerblockchainrpccommands
(crpctable &t)
}
註冊完成之後,當我們呼叫具體的某乙個rpc命令時,會發生什麼呢?我們拿blockchain中的getblockchaininfo來舉例,具體實現如下:
univalue getblockchaininfo(const config &config, const jsonrpcrequest &request)
lock(cs_main);
univalue obj(univalue::vobj);
obj.push_back(pair("chain", params().networkidstring()));
obj.push_back(pair("blocks", int(chainactive.height())));
obj.push_back(
pair("headers", pindexbestheader ? pindexbestheader->nheight : -1));
obj.push_back(
pair("bestblockhash", chainactive.tip()->getblockhash().gethex()));
obj.push_back(pair("difficulty", double(getdifficulty(chainactive.tip()))));
obj.push_back(
pair("mediantime", int64_t(chainactive.tip()->getmediantimepast())));
obj.push_back(
pair("verificationprogress",
guessverificationprogress(params().txdata(), chainactive.tip())));
obj.push_back(pair("chainwork", chainactive.tip()->nchainwork.gethex()));
obj.push_back(pair("pruned", fprunemode));
const consensus::params &consensusparams = params().getconsensus();
cblockindex *tip = chainactive.tip();
univalue softforks(univalue::varr);
univalue bip9_softforks(univalue::vobj);
softforks.push_back(softforkdesc("bip34", 2, tip, consensusparams));
softforks.push_back(softforkdesc("bip66", 3, tip, consensusparams));
softforks.push_back(softforkdesc("bip65", 4, tip, consensusparams));
bip9softforkdescpushback(bip9_softforks, "csv", consensusparams,
consensus::deployment_csv);
obj.push_back(pair("softforks", softforks));
obj.push_back(pair("bip9_softforks", bip9_softforks));
if (fprunemode)
obj.push_back(pair("pruneheight", block->nheight));
}return obj;
如上,每乙個rpc命令在他們相應的模組中,都有基於這個rpc所表述含義的相應**實現。
我們看到getblockchaininfo這個函式返回值是univalue,univalue是cpp中乙個json解析庫,用來將json格式的rpc命令轉化為網路可識別的。轉化實現主要在client.cpp
中進行了簡單的包裝,具體實現如下:
class
crpcconvertparam ;
我們會看到有時候會有相同的methodname,但是他們的paramidx不一樣,所代表的描述資訊也就不同,這裡要注意區分
clint.cpp
主要是服務於bitcoin-cli
客戶端在這邊將json的命令進行序列化之後,傳送給對應的服務端(各個不同的模組),服務端在做相應的處理。
在protocol.h中,我們定義了一些http的狀態碼,以及rpc的錯誤碼,json的格式規範遵循的是rfc4627。
位元幣原始碼解析 3 準備知識 Boost
boost中test模組是用來給 做單元測試的,測試的方法是白盒測試,所以編寫測試必須對待測試的模組有深度的理解,然後再對正常功能和可能會出現的問題進行測試,測試的實際過程就是給定輸入判定是否和預期的輸出相同,所以test本質上也是個驗證等式的工具外加了一層包裝。測試過程中三個主要的工具是boost...
位元幣原始碼解析之初始化
本文主要描述了程序啟動時節點位址 區塊資訊和錢包資訊的初始化 節點執行緒和礦工挖礦執行緒在後續 位元幣原始碼解析之執行緒處理 一文中介紹,孤立塊處理在後續 位元幣原始碼解讀之挖礦 一文中介紹 初始化流程圖如下所示 1 首先呼叫caddrdb類的loadaddresses 函式 同時caddrdb的建...
位元幣原始碼解讀一
上次在ubuntu系統中將位元處原始碼編譯環境設定好了後,還沒有具體分析裡面的 今天我們就解讀一下。原始碼版本是bitcoin 0.9.5rc2。我們說驗證位元幣客戶端安裝成功就是從 which bitcoind 這個命令進行驗證的,因為位元幣客戶端有兩個。乙個是圖形介面的版本,通常被稱為 bitc...