bitcoin原始碼分析
1 必讀–如何閱讀org文件
- 如何開啟org文件
- 可以直接在github上開啟org文件,但是為了方便的跳轉到原始碼,請使用Emacs編輯器開啟org文件!Windows環境的下載連結,Linux下直接使用apt-get或者yum直接安裝emacs即可;
- 如何使用TAB按鍵
- 在*和**、***以及更多星號開頭的標題上敲擊鍵盤上的Tab按鍵,可以展開和隱藏這個標題裡的內容;
- 如何理解行首的冒號
- 行首的冒號(:)是方便org文件輸出到HTML文件重點標註程式碼和命令,除了在#+BEGINEXAMPLE和#+ENDEXAMPLE裡原樣輸出;
- 如何生成HTML文件
- 選單欄裡選擇Org->Export/Publish,會呼叫匯出HTML的選項;
- 如何連結到原始碼
- 比如file:~/.bitcoin ,將游標移動到file開頭的連結上,滑鼠點選,就會自動跳轉到原始碼了,如果是目錄,就會開啟目錄。
- 下載bitcoin原始碼
- 從github網站上直接下載或者使用命令列工具:
git clone --branch v0.8.2 https://github.com/bitcoin/bitcoin.git
注意將bitcoin原始碼目錄放在~目錄下,目錄名為bitcoin,以便迅速在Emacs編輯器中開啟bitcoin原始碼,Windows下的目錄一般為:
C:\Documents and Settings\Administrator\Applicatin Data
如果沒有這個目錄,可以用如下命令檢視目錄路徑
set appdata
2 準備工作
假設你已經對STL及gdb有了一些基本認識,熟悉C++程式設計。
2.1 1.產生除錯資訊
在configure.ac檔案裡增加2行程式碼(注意:行首有冒號):
AC_INIT([Bitcoin]... : ${CFLAGS="-g -ggdb"} : ${CXXFLAGS="-g -ggdb"}
按照doc/build-unix.md檔案裡的的要求重新配置並編譯:
./autogen.sh ./configure make -B //如果是第一次編譯,不需要-B
這樣在輸出的.o檔案及elf檔案裡就會包含有除錯資訊,否則預設會使用-O2優化選項。
2.2 2.gdb裡增加對stl的支援
bitcoin裡大量使用了stl,方便在Linux、Windows、Mac間移植。 7.0以後的gdb已經增加了對Python的支援,通過Python,增加gdb對STL的支援: http://sourceware.org/gdb/wiki/STLSupport
3 開始分析
3.1 靜態分析
3.1.1 配置檔案、快鏈資料檔案、索引檔案及相關目錄:
在root下有一個目錄.bitcoin,首先介紹下幾個檔案: file:~/.bitcoin
- bitcoin.conf
- 配置檔案,bitcoind啟動的時候會讀取這個檔案
- debug.log
- 除錯資訊輸出到這個檔案裡
- peers.dat
- 儲存的其他peer的資訊
- wallet.dat
- 錢包檔案
- blocks
- 快鏈(block chain)儲存的地方
- chainstate
- 快鏈的狀態
- testnet3
- 用於測試的快鏈,有一個不同的起始塊(genesis block),testnet1中的起始塊,就是目前大家在交易的塊鏈。
3.1.2 原始碼檔案說明:
.h檔案及.cpp檔案中類的定義及說明: Doxygen自動產生的說明
3.2 動態分析
3.2.1 配置bitcoin.conf
testnet=1
使用測試網路,詳見bitcoin.conf的詳細配置
3.2.2 開始除錯
gdb bitcoind
好了,從bitcoind裡讀取symbol的時間可能會稍微長點:
Reading symbols from bitcoin/src/bitcoind...done.
開始除錯:
b main run
int main(int argc, char* argv[]) { bool fRet = false; // Connect bitcoind signal handlers noui_connect(); fRet = AppInit(argc, argv); if (fRet && fDaemon) return 0; return (fRet ? 0 : 1); }
main()函式的重點是AppInit(),nouiconnect()是用來連結前端Qt的初始化程式,圖形介面我們就不分析了,直接進入到AppInit()
1. bool AppInit(int argc, char* argv[]) 2. { 3. boost::thread_group threadGroup;//使用boost的多執行緒,使得前端Qt程式執行流暢 4. boost::thread* detectShutdownThread = NULL;//在程式啟動期間,如果按了Ctrl-C鍵退出,則讓多執行緒處理完後退出 5. bool fRet = false; 6. try 7. { 8. // 9. // Parameters 10. // 11. // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() 12. ParseParameters(argc, argv); 13. if (!boost::filesystem::is_directory(GetDataDir(false))) 14. { 15. fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); 16. return false; 17. } 18. ReadConfigFile(mapArgs, mapMultiArgs); 19. // Check for -testnet or -regtest parameter (TestNet() calls are only valid after this clause) 20. if (!SelectParamsFromCommandLine()) { 21. fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); 22. return false; 23. } 24. if (mapArgs.count("-?") || mapArgs.count("--help")) 25. { 26. // First part of help message is specific to bitcoind / RPC client 27. std::string strUsage = _("Bitcoin Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n\n" + 28. _("Usage:") + "\n" + 29. " bitcoind [options] " + _("Start Bitcoin server") + "\n" + 30. _("Usage (deprecated, use bitcoin-cli):") + "\n" + 31. " bitcoind [options] <command> [params] " + _("Send command to Bitcoin server") + "\n" + 32. " bitcoind [options] help " + _("List commands") + "\n" + 33. " bitcoind [options] help <command> " + _("Get help for a command") + "\n"; 34. strUsage += "\n" + HelpMessage(HMM_BITCOIND); 35. strUsage += "\n" + HelpMessageCli(false); 36. fprintf(stdout, "%s", strUsage.c_str()); 37. return false; 38. } 39. // Command-line RPC 40. bool fCommandLine = false; 41. for (int i = 1; i < argc; i++) 42. if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:")) 43. fCommandLine = true; 44. if (fCommandLine) 45. { 46. int ret = CommandLineRPC(argc, argv); 47. exit(ret); 48. } 49. #ifndef WIN32 50. fDaemon = GetBoolArg("-daemon", false); 51. if (fDaemon) 52. { 53. fprintf(stdout, "Bitcoin server starting\n"); 54. // Daemonize 55. pid_t pid = fork(); 56. if (pid < 0) 57. { 58. fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); 59. return false; 60. } 61. if (pid > 0) // Parent process, pid is child process id 62. { 63. CreatePidFile(GetPidFile(), pid); 64. return true; 65. } 66. // Child process falls through to rest of initialization 67. pid_t sid = setsid(); 68. if (sid < 0) 69. fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); 70. } 71. #endif 72. SoftSetBoolArg("-server", true); 73. detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); 74. fRet = AppInit2(threadGroup); 75. } 76. catch (std::exception& e) { 77. PrintExceptionContinue(&e, "AppInit()"); 78. } catch (...) { 79. PrintExceptionContinue(NULL, "AppInit()"); 80. }
程式碼裡增加了一些中文註釋,作為對英文註釋的補充。
- try..catch{}方式處理這段程式碼,因為這裡涉及大量磁碟讀取,有可能碰到無法開啟檔案,磁碟空間滿等其他問題。
- ParseParameters()及ReadConfigFile()都是在讀取一些引數,需要注意的是,命令列引數的優先順序高於配置檔案,比如執行了如下語句
bitcoind -testnet=0
則在bitcoin.conf中配置的testnet則無效了。
引數最終儲存在mapArgs及mapMultiArgs中,定義如下:
map<string, string> mapArgs; map<string, vector<string> > mapMultiArgs;
使用了STL的map定義的,後面會經常使用這幾個函式GetBoolArg()、GetArg()來檢視引數設定,如:
bool GetBoolArg(const std::string& strArg, bool fDefault) { if (mapArgs.count(strArg)) { if (mapArgs[strArg].empty()) return true; return (atoi(mapArgs[strArg]) != 0); } return fDefault; }
經常會看到這樣呼叫GetBoolArg():
GetBoolArg("-daemon", false)
如果沒有找到相關設定,則返回假。
- 24行到38行是列印幫助提示。
- 介紹下面的內容前,先介紹下RPC(遠端過程呼叫),命令列方式下RPC的使用方法:
bitcoind -daemon //後臺執行 bitcoind getinfo //獲取狀態資訊 bitcoind -stop//停止daemon程式
- 39行到48行判斷是否是上面第二行(getinfo)的語句:首先判斷引數是否有'-'或'/',並且不包含'bitcoin:',則執行RPC(使用的是JSON-RPC呼叫協議)
bitcoin:URI是用於請款的,所以應該排除這種情況: bitcoin://1F2EUzKR1XsLRCtEnsnpDQZ13XJgS6P3ZK?amount=0.001&message=donation
接著執行CommandLineRPC(),具體執行RPC(遠端過程呼叫).
- 49行到71行是處理-daemon引數,假設這樣執行bitcond
bitcoind -daemon
通過fork()建立一個子程式,建立成功,父程式則在64行的時候返回,子程式接著執行下面的初始化,包括AppInit2()。 在.bitcoin目錄下建立了一個bitcoind.pid的檔案,記錄了子程式的PID。
file:~/bitcoin/src/init.cpp::bool AppInit2 boost threadgroup threadGroup
/** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ bool AppInit2(boost::thread_group& threadGroup) { // ****************** Step 1: setup 設定 ... // ****************** Step 2: parameter interactions 引數互動(主要是一些引數設定) ... // ****************** Step 3: parameter-to-internal-flags 引數傳入內部標記(bool型變數) ... // ****************** Step 4: application initialization: dir lock, daemonize, pidfile, debug log 應用初始化:鎖定目錄,後臺執行,除錯資訊 ... // ****************** Step 5: verify wallet database integrity 確認錢包資料庫的完整性 ... // ****************** Step 6: network initialization 網路初始化 ... // ****************** Step 7: load block chain 載入塊鏈 ... // ****************** Step 8: load wallet 載入錢包 ... // ****************** Step 9: import blocks 匯入塊資料 ... // ****************** Step 10: load peers 匯入peers ... // ****************** Step 11: start node 開始節點(挖礦程式在這裡) ... // ****************** Step 12: finished 完成 ... }
- AppInit2裡包含了bitcoin的大部分初始程式,包括讀取'塊索引'、載入塊鏈、載入100個預產生的keys,匯入peers.dat中的資訊,以及初始化其他執行緒。
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- 比特幣原始碼研讀(1)bitcoin原始碼結構比特幣原始碼
- 帶你瞭解比特幣Bitcoin原始碼比特幣原始碼
- Bitcoin程式碼之MerkleTree
- 比特幣原始碼研讀(0)bitcoin本地編譯與使用比特幣原始碼編譯
- Retrofit原始碼分析三 原始碼分析原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 【Android原始碼】Fragment 原始碼分析Android原始碼Fragment
- 【Android原始碼】Intent 原始碼分析Android原始碼Intent
- k8s client-go原始碼分析 informer原始碼分析(6)-Indexer原始碼分析K8SclientGo原始碼ORMIndex
- k8s client-go原始碼分析 informer原始碼分析(4)-DeltaFIFO原始碼分析K8SclientGo原始碼ORM
- 以太坊原始碼分析(20)core-bloombits原始碼分析原始碼OOM
- 以太坊原始碼分析(24)core-state原始碼分析原始碼
- 以太坊原始碼分析(29)core-vm原始碼分析原始碼
- 【MyBatis原始碼分析】select原始碼分析及小結MyBatis原始碼
- redis原始碼分析(二)、redis原始碼分析之sds字串Redis原始碼字串
- ArrayList 原始碼分析原始碼
- kubeproxy原始碼分析原始碼
- [原始碼分析]ArrayList原始碼
- redux原始碼分析Redux原始碼
- preact原始碼分析React原始碼
- Snackbar原始碼分析原始碼
- React原始碼分析React原始碼
- CAS原始碼分析原始碼
- Redux 原始碼分析Redux原始碼
- SDWebImage 原始碼分析Web原始碼
- Aspects原始碼分析原始碼