比特币源码剖析(十六)
本篇主要分析 Step 11: start node 第十一步启动节点服务的详细过程。
源码剖析
3.11.11.第十一步,启动节点服务相关线程。这部分代码实现在“init.cpp”文件的 AppInit2(…) 函数中。
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // 3.11.程序初始化,共 12 步
{
...
// ********************************************************* Step 11: start node // 启动节点服务,监听网络 P2P 请求,挖矿线程
if (!CheckDiskSpace()) // 1.检测硬盘剩余空间是否充足(最少 50MB),用于接收并存储新区块
return false; // 若空间不足,返回 false 退出
if (!strErrors.str().empty()) // 2.检查前面的初始化过程是否有错误信息
return InitError(strErrors.str()); // 若存在错误信息,返回错误信息并退出
RandAddSeedPerfmon(); // 3.用于给钱包生成随机私钥种子
//// debug print // 4.调试打印,记录相关信息
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); // 区块索引大小
LogPrintf("nBestHeight = %d\n", chainActive.Height()); // 最佳区块链高度
#ifdef ENABLE_WALLET
LogPrintf("setKeyPool.size() = %u\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); // 钱包内密钥池大小
LogPrintf("mapWallet.size() = %u\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); // 钱包交易列表大小
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); // 钱包内地址簿的大小
#endif
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) // 5.监听洋葱路由,默认打开
StartTorControl(threadGroup, scheduler);
StartNode(threadGroup, scheduler); // 6.启动各种线程
// Monitor the chain, and alert if we get blocks much quicker or slower than expected
// The "bad chain alert" scheduler has been disabled because the current system gives far
// too many false positives, such that users are starting to ignore them.
// This code will be disabled for 0.12.1 while a fix is deliberated in #7568
// this was discussed in the IRC meeting on 2016-03-31.
//
// --- disabled ---
//int64_t nPowTargetSpacing = Params().GetConsensus().nPowTargetSpacing;
//CScheduler::Function f = boost::bind(&PartitionCheck, &IsInitialBlockDownload,
// boost::ref(cs_main), boost::cref(pindexBestHeader), nPowTargetSpacing);
//scheduler.scheduleEvery(f, nPowTargetSpacing);
// --- end disabled ---
// Generate coins in the background // 7.挖矿作用:产生比特币,记录交易
GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams); // 创建挖矿线程,默认关闭,线程数默认为 1(0 表示禁止挖矿,-1 表示 CPU 核数)
...
}
基本流程:
- 检查硬盘可用空间是否充足。
- 检查前面 10 步初始化过程是否存在错误。
- 设置随机数种子,用于钱包生成私钥。
- 记录区块链、钱包的相关数据大小。
- 创建洋葱路由监听线程。
- 启动比特币节点相关线程。
- 创建比特币 CPU 挖矿线程。
1.调用 CheckDiskSpace() 检测硬盘剩余空间是否充足(最低 50MB),用于接收并存储新区块。 该函数声明在“main.h”文件中。
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); // 检查硬盘空间接收一个新区块是否充足
实现在“main.cpp”文件中,入参为:0。
/** Abort with a message */ // 反馈信息并关闭节点服务
bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
{
strMiscWarning = strMessage;
LogPrintf("*** %s\n", strMessage); // 记录日志
uiInterface.ThreadSafeMessageBox(
userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage,
"", CClientUIInterface::MSG_ERROR); // UI 显示错误信息
StartShutdown(); // 关闭节点服务
return false;
}
...
bool CheckDiskSpace(uint64_t nAdditionalBytes) // 0
{
uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; // 1.获取数据目录所在磁盘的剩余(可用)空间的大小,单位 Byte
// Check for nMinDiskSpace bytes (currently 50MB) // 2.检查硬盘最小空间(当前为 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) // 磁盘剩余空间小于 50MB
return AbortNode("Disk space is low!", _("Error: Disk space is low!")); // 返回相应信息并关闭节点
return true; // 若剩余空间充足,则返回 true
}
1.1.获取当前硬盘可用空间。
1.2.判断空间是否低于最小硬盘空间阈值。
2.调用 InitError(strErrors.str()) 输出错误信息并退出。 该函数实现在“init.cpp”文件中,入参为:错误信息字符串。
bool static InitError(const std::string &str)
{
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); // 弹出消息框,提示用户
return false;
}
5.调用 StartTorControl(threadGroup, scheduler) 启动洋葱路由服务线程。 该函数声明在“torcontrol.h”文件中。
void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler); // 启动洋葱路由服务
实现在“torcontrol.cpp”文件中,入参为:线程组对象,调度器对象。
/****** Thread ********/
struct event_base *base;
boost::thread torControlThread;
static void TorControlThread()
{
TorController ctrl(base, GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); // 创建洋葱路由控制器对象
event_base_dispatch(base);
}
void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler)
{
assert(!base);
#ifdef WIN32
evthread_use_windows_threads();
#else
evthread_use_pthreads();
#endif
base = event_base_new();
if (!base) {
LogPrintf("tor: Unable to create event_base\n");
return;
}
torControlThread = boost::thread(boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); // 创建洋葱路由控制线程
}
类 TorController 定义在“torcontrol.cpp”文件中。
/** Low-level handling for Tor control connection.
* Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
*/ // 洋葱路由控制连接的低级处理。说出类似 SMTP 的协议。
class TorControlConnection
{
public:
typedef boost::function<void(TorControlConnection&)> ConnectionCB;
typedef boost::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
/** Create a new TorControlConnection.
*/ // 创建一个新的洋葱路由控制连接对象。
TorControlConnection(struct event_base *base);
~TorControlConnection();
/**
* Connect to a Tor control port.
* target is address of the form host:port.
* connected is the handler that is called when connection is succesfully established.
* disconnected is a handler that is called when the connection is broken.
* Return true on success.
*/ // 连接到洋葱路由控制端口。target 是“主机:端口”形式的地址。connected 是连接成功建立时调用的处理函数。disconnected 是连接断开时调用的处理函数。成功返回 true。
bool Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected);
/**
* Disconnect from Tor control port.
*/ // 断开洋葱路由控制端口。
bool Disconnect();
/** Send a command, register a handler for the reply.
* A trailing CRLF is automatically added.
* Return true on success.
*/ // 发送一条命令,注册响应的处理函数。自动在尾部添加回车换行 CRLF(即\r\n)
bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
...
/** Callback when ready for use */ // 用于连接准备的回调
boost::function<void(TorControlConnection&)> connected;
/** Callback when connection lost */ // 断开连接时的回调
boost::function<void(TorControlConnection&)> disconnected;
/** Libevent event base */
struct event_base *base; // Libevent 事件基指针
/** Connection to control socket */
struct bufferevent *b_conn; // 连接到控制套接字
/** Message being received */
TorControlReply message; // 接收的信息
/** Response handlers */ // 响应处理函数
std::deque<ReplyHandlerCB> reply_handlers; // 处理函数队列
...
};
...
bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected)
{
if (b_conn) // 若连接已存在
Disconnect(); // 先断开连接
// Parse target address:port // 解析目标端口
struct sockaddr_storage connect_to_addr;
int connect_to_addrlen = sizeof(connect_to_addr);
if (evutil_parse_sockaddr_port(target.c_str(),
(struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) { // 解析目标地址和目标端口
LogPrintf("tor: Error parsing socket address %s\n", target);
return false;
}
// Create a new socket, set up callbacks and enable notification bits // 创建一个新的套接字,设置回调并设置通知位
b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); // 设置新套接字
if (!b_conn)
return false;
bufferevent_setcb(b_conn, TorControlConnection::readcb, NULL, TorControlConnection::eventcb, this); // 设置回调函数
bufferevent_enable(b_conn, EV_READ|EV_WRITE);
this->connected = connected; // 设置连接回调函数
this->disconnected = disconnected; // 设置断开连接回调函数
// Finally, connect to target // 最终,连接目标
if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) { // 连接套接字地址
LogPrintf("tor: Error connecting to address %s\n", target);
return false;
}
return true;
}
...
bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler)
{
if (!b_conn)
return false;
struct evbuffer *buf = bufferevent_get_output(b_conn); // 获取输出缓冲区
if (!buf)
return false;
evbuffer_add(buf, cmd.data(), cmd.size()); // 缓冲区追加命令
evbuffer_add(buf, "\r\n", 2); // 追加 "\r\n"
reply_handlers.push_back(reply_handler); // 加入响应函数处理队列
return true;
}
...
/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occured, status will be false, otherwise status will be true and the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/ // 读取某文件的整个内容并以字符串形式返回获取的内容。返回一个对 <状态,读取的字符串>
static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
FILE *f = fopen(filename.c_str(), "rb"); // 以 2 进制只读模式打开文件
if (f == NULL) // 若打开失败
return std::make_pair(false,""); // 返回<false,空串>对
std::string retval; // 待获取的字符串类型的文件内容
char buffer[128]; // 128 字节的缓冲区
size_t n; // 保存实际读取的字节数
while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) { // 读取 128 字节到 buffer,若到文件尾,下次实际读入字节为 0
retval.append(buffer, buffer+n); // 把实际读取的字节追加到返回值中
if (retval.size() > maxsize) // 当返回值字符串大小大于最大值时
break; // 跳出
}
fclose(f); // 关闭文件
return std::make_pair(true,retval); // 返回成功读取的内容<true,文件内容字符串>
}
...
/** Controller that connects to Tor control socket, authenticate, then create
* and maintain a ephemeral hidden service.
*/ // 控制器连接到洋葱路由控制套接字,然后进行身份验证,然后创建并维持一个短暂的隐藏服务。
class TorController
{
public:
TorController(struct event_base* base, const std::string& target);
~TorController();
/** Get name fo file to store private key in */
std::string GetPrivateKeyFile(); // 获取(拼接)洋葱路由私钥文件名
/** Reconnect, after getting disconnected */
void Reconnect(); // 断开连接后重连
private:
struct event_base* base; // 事件基对象指针
std::string target; // 目标
TorControlConnection conn; // 洋葱路由控制连接对象
std::string private_key; // 洋葱路由私钥
std::string service_id;
bool reconnect; // 再连接标志
struct event *reconnect_ev; // 再连接事件对象指针
float reconnect_timeout; // 再连接超时时间
CService service; // 服务对象(IP+Port)
...
/** Callback for PROTOCOLINFO result */ // 协议信息结果回调函数
void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
/** Callback after succesful connection */ // 成功连接后的回调函数
void connected_cb(TorControlConnection& conn);
/** Callback after connection lost or failed connection attempt */ // 连接断开或失败的连接尝试后的回调函数
void disconnected_cb(TorControlConnection& conn);
...
};
TorController::TorController(struct event_base* base, const std::string& target):
base(base), // 初始化事件基对象
target(target), conn(base), reconnect(true), reconnect_ev(0),
reconnect_timeout(RECONNECT_TIMEOUT_START) // 设定再连接超时时间
{
// Start connection attempts immediately // 立刻开始尝试连接
if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1),
boost::bind(&TorController::disconnected_cb, this, _1) )) { // 连接洋葱路由控制端口
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", target);
}
// Read service private key if cached // 如果已经缓存,则读取服务私钥
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile()); // 读取洋葱路由私钥文件内容
if (pkf.first) { // 若读取成功
LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile());
private_key = pkf.second; // 设置私钥
}
}
...
void TorController::connected_cb(TorControlConnection& conn)
{
reconnect_timeout = RECONNECT_TIMEOUT_START; // 设置重连超时时间
// First send a PROTOCOLINFO command to figure out what authentication is expected // 首先发送一条协议信息命令,用于找出预期的身份验证
if (!conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) // 发送 "PROTOCOLINFO 1" 命令
LogPrintf("tor: Error sending initial protocolinfo command\n");
}
void TorController::disconnected_cb(TorControlConnection& conn)
{
// Stop advertizing service when disconnected // 当断开连接时,停止广告服务
if (service.IsValid()) // 若服务有效
RemoveLocal(service); // 移除本地服务
service = CService(); // 创建服务空对象
if (!reconnect) // 若重连关闭
return; // 直接返回
LogPrint("tor", "tor: Not connected to Tor control port %s, trying to reconnect\n", target);
// Single-shot timer for reconnect. Use exponential backoff. // 用于重连的单次计时器。使用指数回退
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); // 创建新的事件对象
event_add(reconnect_ev, &time); // 添加事件对象和时间间隔
reconnect_timeout *= RECONNECT_TIMEOUT_EXP;
}
...
std::string TorController::GetPrivateKeyFile()
{
return (GetDataDir() / "onion_private_key").string(); // 返回凭借的数据文件名字符串
}