1. 比特幣簡介
比特幣(BitCoin)的概念最初由中本聰在2009年提出,根據中本聰的思路設計發佈的開源軟件以及建構其上的P2P網絡。比特幣是一種P2P形式的數字貨幣。點對點的傳輸意味着一個去中心化的支付系統。
與大多數貨幣不同,比特幣不依靠特定貨幣機構發行,它依據特定算法,通過大量的計算產生,比特幣經濟使用整個P2P網絡中衆多節點構成的分佈式數據庫來確認並記錄所有的交易行爲,並使用密碼學的設計來確保貨幣流通各個環節安全性。P2P的去中心化特性與算法本身可以確保無法通過大量製造比特幣來人爲操控幣值。基於密碼學的設計可以使比特幣只能被真實的擁有者轉移或支付。這同樣確保了貨幣所有權與流通交易的匿名性。比特幣與其他虛擬貨幣最大的不同,是其總數量非常有限,具有極強的稀缺性。該貨幣系統曾在4年內只有不超過1050萬個,之後的總數量將被永久限制在2100萬個。
2. 比特幣整體架構
點對點網絡,每一個網絡中的節點即是Client又是Server,如下圖所示:
* 節點和節點之間通過發送消息命令來相互通信的,下圖是對應的消息命令格式:
CMessageHeader對應的類圖如下所示:
* 節點通信過程中使用的對應的消息命令如下表所示:
命令 | 消息內容 | 說明 |
---|---|---|
version | nVersion,nService,nTime,address | 版本,服務標識,時間;地址 |
addr | vector< CAddress> | 地址列表 |
inv | vector< CInv> | 庫存信息列表:將對應的庫存發送消息增加到庫存發送已知中 |
getdata | vector< CInv> | 根據inv對應的type執行不同的處理,就是將對應的請求消息轉化爲待請求的命令放入到節點對應的發送消息緩存中 |
getblocks | CBlockLocator locator;uint256 hashStop; | 根據locator定位區塊在鏈上位置,從這個位置開始往後找(找對應的next),一直到對應塊的block的hash等於hashStop爲止,並將所有找到的區塊發送庫存setInventoryKnown2中等待被髮送 |
Tx | CTransaction | 將交易消息放入到對應的已知庫存中,如果此交易能夠被接受,則對此消息進行轉播,並遞歸處理所有依賴這個交易對應的孤兒交易;如果交易不被接受,則將次交易放入到孤兒交易列表中mapOrphanTransactions和mapOrphanTransactionsByPrev中 |
block | CBlock | 塊消息:將接收的block放入對應的已知庫存中,並對應這個塊進行處理,並將此塊從mapAlreadyAskedFor諮詢中移除 |
3. 比特幣整體處理流程
對應的整體流程如下圖所示:
3.1 LoadAdreess方法
LoadAdreess方法從地址文件addr.dat中讀取對應的地址信息,放入對應的全局內存對象mapAddresses中,其中addr.dat對應的結構如下:
Key | Value | 說明 | 內存中存放對象 |
---|---|---|---|
addr | CAddress | 數據庫中保存對應的地址新 | map< vector,CAddress>mapAddresses節點地址映射:key對應的是ip地址+端口,value是CAddress對象 |
3.2 LoadBlockIndex方法
LoadBlockIndex方法從塊索引文件blkindex.dat中讀取對應的塊索引信息,放入對應的全局內存對象mapBlockIndex中,其中blkindex.dat對應的結構如下:
Key | Value | 說明 | 內存中存放對象 |
---|---|---|---|
blockindex | CDiskBlockIndex | 塊索引數據庫 | map< uint256, CBlockIndex*> mapBlockIndex塊索引信息:其中key對應的block的hash值 |
對應的源碼如下:
bool LoadBlockIndex(bool fAllowNew)
{
//
// Load block index
//
CTxDB txdb("cr");
if (!txdb.LoadBlockIndex())
return false;
txdb.Close();
//
// Init with genesis block
//
if (mapBlockIndex.empty())
{
if (!fAllowNew)
return false;
// Genesis Block:
// GetHash() = 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
// hashMerkleRoot = 0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
// txNew.vin[0].scriptSig = 486604799 4 0x736B6E616220726F662074756F6C69616220646E6F63657320666F206B6E697262206E6F20726F6C6C65636E61684320393030322F6E614A2F33302073656D695420656854
// txNew.vout[0].nValue = 5000000000
// txNew.vout[0].scriptPubKey = 0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704 OP_CHECKSIG
// block.nVersion = 1
// block.nTime = 1231006505
// block.nBits = 0x1d00ffff
// block.nNonce = 2083236893
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
// CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
// CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
// vMerkleTree: 4a5e1e
// Genesis block
char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
CTransaction txNew;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].nValue = 50 * COIN;
txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG;
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
block.nTime = 1231006505;
block.nBits = 0x1d00ffff;
block.nNonce = 2083236893;
//// debug print, delete this later
printf("%s\n", block.GetHash().ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str());
txNew.vout[0].scriptPubKey.print();
block.print();
assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
assert(block.GetHash() == hashGenesisBlock);
// Start new block file
unsigned int nFile;
unsigned int nBlockPos;
if (!block.WriteToDisk(!fClient, nFile, nBlockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
}
return true;
}
3.3 LoadWallet方法
LoadWallet方法從文件wallet.dat中讀取對應的錢包交易信息和其他的配置信息,放入到不同的全局內存對象中,其中wallet.dat對應的格式(key-value結構)如下所示:
Key | Value | 說明 | 內存中存放對象 | |
---|---|---|---|---|
Type | Key1 | Key分爲類型類型和key | ||
name | 比特幣地址 | 比特幣地址對應名稱 | 地址和名稱之間映射 | map< string,string> mapAddressBook地址和名稱的映射,其中key爲地址,value爲名稱 |
Tx | 交易hash值 | 錢包交易對象CWalletTx | 交易hash與交易之間映射 | map< uint256, CWalletTx>mapWalle錢包交易對應的map,其中key對應的錢包交易的hash值 |
Key | 公鑰 | 私鑰 | 公鑰和私鑰對應關係 | map< vector< unsigned char>, CPrivKey> mapKeys公鑰和私鑰對應的映射關係,其中key爲公鑰,value爲私鑰;map< uint160, vector > mapPubKeys公鑰的hash值和公鑰的關係,其中key爲公鑰的hash值,value爲公鑰 |
defaultkey | vector< unsigned char> vchDefaultKeyRet | 默認key對應對應的值 | ||
setting | fGenerateBitcoins | 是否產生比特幣標記 | 是否挖礦標記 | int fGenerateBitcoins |
setting | nTransactionFee | 交易手續費的值 | 交易手續費 | int64 nTransactionFee |
setting | addrIncoming | CAddress對象 | 獲得當前對應的外部地址,用於接收外部的連接 | CAddress addrIncoming |
源碼內容如下:
//
// CWalletDB
//
bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
{
vchDefaultKeyRet.clear();
//// todo: shouldn't we catch exceptions and try to recover and continue?
CRITICAL_BLOCK(cs_mapKeys)
CRITICAL_BLOCK(cs_mapWallet)
{
// Get cursor
Dbc* pcursor = GetCursor();
if (!pcursor)
return false;
loop
{
// Read next record
CDataStream ssKey;
CDataStream ssValue;
int ret = ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
return false;
// Unserialize
// Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other
string strType;
ssKey >> strType;
if (strType == "name")
{
string strAddress;
ssKey >> strAddress;
ssValue >> mapAddressBook[strAddress];
}
else if (strType == "tx")
{
uint256 hash;
ssKey >> hash;
CWalletTx& wtx = mapWallet[hash];
ssValue >> wtx;
if (wtx.GetHash() != hash)
printf("Error in wallet.dat, hash mismatch\n");
//// debug print
//printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
//printf(" %12I64d %s %s %s\n",
// wtx.vout[0].nValue,
// DateTimeStr(wtx.nTime).c_str(),
// wtx.hashBlock.ToString().substr(0,14).c_str(),
// wtx.mapValue["message"].c_str());
}
else if (strType == "key")
{
vector<unsigned char> vchPubKey;
ssKey >> vchPubKey;
CPrivKey vchPrivKey;
ssValue >> vchPrivKey;
mapKeys[vchPubKey] = vchPrivKey;
mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
}
else if (strType == "defaultkey")
{
ssValue >> vchDefaultKeyRet;
}
else if (strType == "setting") /// or settings or option or options or config?
{
string strKey;
ssKey >> strKey;
if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins;
if (strKey == "nTransactionFee") ssValue >> nTransactionFee;
if (strKey == "addrIncoming") ssValue >> addrIncoming;
}
}
}
printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
printf("nTransactionFee = %I64d\n", nTransactionFee);
printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());
return true;
}
3.4節點通信線程
啓動了三個主要的線程,ThreadOpenConnections,ThreadSocketHandler和ThreadMessageHandler,這個三個線程對應的主要處理流程分別如下圖所示:
3.4.1線程ThreadOpenConnections
線程ThreadOpenConnections對應的處理流程如下圖所示:
源碼如下:
// 對於每一個打開節點的鏈接,進行節點之間信息通信,獲得節點對應的最新信息,比如節點對應的知道地址進行交換等
void ThreadOpenConnections2(void* parg)
{
printf("ThreadOpenConnections started\n");
// 初始化網絡連接
// Initiate network connections
const int nMaxConnections = 15; // 最大連接數
loop
{
// Wait
vfThreadRunning[1] = false;
Sleep(500);
while (vNodes.size() >= nMaxConnections || vNodes.size() >= mapAddresses.size())
{
CheckForShutdown(1);
Sleep(2000);
}
vfThreadRunning[1] = true;
CheckForShutdown(1);
// Ip對應的C類地址,相同的C類地址放在一起
// Make a list of unique class C's
unsigned char pchIPCMask[4] = { 0xff, 0xff, 0xff, 0x00 };
unsigned int nIPCMask = *(unsigned int*)pchIPCMask;
vector<unsigned int> vIPC;
CRITICAL_BLOCK(cs_mapAddresses)
{
vIPC.reserve(mapAddresses.size());
unsigned int nPrev = 0;
// mapAddress已經進行排序了,默認是生效排序
foreach(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
{
const CAddress& addr = item.second;
if (!addr.IsIPv4())
continue;
// Taking advantage of mapAddresses being in sorted order,
// with IPs of the same class C grouped together.
unsigned int ipC = addr.ip & nIPCMask;
if (ipC != nPrev)
vIPC.push_back(nPrev = ipC);
}
}
// IP選擇的過程
// The IP selection process is designed to limit vulnerability致命性 to address flooding.
// Any class C (a.b.c.?) has an equal chance of being chosen, then an IP is
// chosen within the class C. An attacker may be able to allocate many IPs, but
// they would normally be concentrated in blocks of class C's. They can hog獨佔 the
// attention within their class C, but not the whole IP address space overall.
// A lone node in a class C will get as much attention as someone holding all 255
// IPs in another class C.
//
bool fSuccess = false;
int nLimit = vIPC.size();
while (!fSuccess && nLimit-- > 0)
{
// Choose a random class C 隨機獲取一個C級別的地址
unsigned int ipC = vIPC[GetRand(vIPC.size())];
// Organize all addresses in the class C by IP
map<unsigned int, vector<CAddress> > mapIP;
CRITICAL_BLOCK(cs_mapAddresses)
{
unsigned int nDelay = ((30 * 60) << vNodes.size());
if (nDelay > 8 * 60 * 60)
nDelay = 8 * 60 * 60;
/*
map::lower_bound(key):返回map中第一個大於或等於key的迭代器指針
map::upper_bound(key):返回map中第一個大於key的迭代器指針
*/
for (map<vector<unsigned char>, CAddress>::iterator mi = mapAddresses.lower_bound(CAddress(ipC, 0).GetKey());
mi != mapAddresses.upper_bound(CAddress(ipC | ~nIPCMask, 0xffff).GetKey());
++mi)
{
const CAddress& addr = (*mi).second;
unsigned int nRandomizer = (addr.nLastFailed * addr.ip * 7777U) % 20000;
// 當前時間 - 地址連接最新失敗的時間 要大於對應節點重連的間隔時間
if (GetTime() - addr.nLastFailed > nDelay * nRandomizer / 10000)
mapIP[addr.ip].push_back(addr); //同一個地址區段不同地址: 同一個地址的不同端口,所有對應同一個ip會有多個地址
}
}
if (mapIP.empty())
break;
// Choose a random IP in the class C
map<unsigned int, vector<CAddress> >::iterator mi = mapIP.begin();
boost::iterators::advance_adl_barrier::advance(mi, GetRand(mapIP.size())); // 將指針定位到隨機位置
// 遍歷同一個ip對應的所有不同端口
// Once we've chosen an IP, we'll try every given port before moving on
foreach(const CAddress& addrConnect, (*mi).second)
{
// ip不能是本地ip,且不能是非ipV4地址,對應的ip地址不在本地的節點列表中
if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip))
continue;
// 鏈接對應地址信息的節點
CNode* pnode = ConnectNode(addrConnect);
if (!pnode)
continue;
pnode->fNetworkNode = true; //設置對應的節點爲網絡節點,是因爲從對應的本地節點列表中沒有查詢到
// 如果本地主機地址能夠進行路由,則需要廣播我們的地址
if (addrLocalHost.IsRoutable())
{
// Advertise our address
vector<CAddress> vAddrToSend;
vAddrToSend.push_back(addrLocalHost);
pnode->PushMessage("addr", vAddrToSend); // 將消息推送出去放入vsend中,在消息處理線程中進行處理
}
// 從創建的節點獲得儘可能多的地址信息,發送消息,在消息處理線程中進行處理
// Get as many addresses as we can
pnode->PushMessage("getaddr");
////// should the one on the receiving end do this too?
// Subscribe our local subscription list
// 新建的節點要訂閱我們本地主機訂閱的對應通斷
const unsigned int nHops = 0;
for (unsigned int nChannel = 0; nChannel < pnodeLocalHost->vfSubscribe.size(); nChannel++)
if (pnodeLocalHost->vfSubscribe[nChannel])
pnode->PushMessage("subscribe", nChannel, nHops);
fSuccess = true;
break;
}
}
}
}
3.4.2線程ThreadSocketHandler
線程ThreadSocketHandler對應的處理流程如下:
源碼如下:
// socket 處理,parag對應的是本地節點開啓的監聽socket
void ThreadSocketHandler2(void* parg)
{
printf("ThreadSocketHandler started\n");
SOCKET hListenSocket = *(SOCKET*)parg; // 獲得監聽socket
list<CNode*> vNodesDisconnected;
int nPrevNodeCount = 0;
loop
{
//
// Disconnect nodes
//
CRITICAL_BLOCK(cs_vNodes)
{
// Disconnect duplicate connections 釋放同一個ip重複鏈接對應的節點,可能是不同端口
map<unsigned int, CNode*> mapFirst;
foreach(CNode* pnode, vNodes)
{
if (pnode->fDisconnect)
continue;
unsigned int ip = pnode->addr.ip;
// 本地主機ip地址對應的是0,所以所有的ip地址都應該大於這個ip
if (mapFirst.count(ip) && addrLocalHost.ip < ip)
{
// In case two nodes connect to each other at once,
// the lower ip disconnects its outbound connection
CNode* pnodeExtra = mapFirst[ip];
if (pnodeExtra->GetRefCount() > (pnodeExtra->fNetworkNode ? 1 : 0))
swap(pnodeExtra, pnode);
if (pnodeExtra->GetRefCount() <= (pnodeExtra->fNetworkNode ? 1 : 0))
{
printf("(%d nodes) disconnecting duplicate: %s\n", vNodes.size(), pnodeExtra->addr.ToString().c_str());
if (pnodeExtra->fNetworkNode && !pnode->fNetworkNode)
{
pnode->AddRef();
swap(pnodeExtra->fNetworkNode, pnode->fNetworkNode);
pnodeExtra->Release();
}
pnodeExtra->fDisconnect = true;
}
}
mapFirst[ip] = pnode;
}
// 斷開不使用的節點
// Disconnect unused nodes
vector<CNode*> vNodesCopy = vNodes;
foreach(CNode* pnode, vNodesCopy)
{
// 節點準備釋放鏈接,並且對應的接收和發送緩存區都是空
if (pnode->ReadyToDisconnect() && pnode->vRecv.empty() && pnode->vSend.empty())
{
// 從節點列表中移除
// remove from vNodes
vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
pnode->Disconnect();
// 將對應準備釋放的節點放在對應的節點釋放鏈接池中,等待對應節點的所有引用釋放
// hold in disconnected pool until all refs are released
pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60); // 向後推遲5分鐘
if (pnode->fNetworkNode)
pnode->Release();
vNodesDisconnected.push_back(pnode);
}
}
// 刪除端口的鏈接的節點:刪除的條件是對應節點的引用小於等於0
// Delete disconnected nodes
list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
foreach(CNode* pnode, vNodesDisconnectedCopy)
{
// wait until threads are done using it
if (pnode->GetRefCount() <= 0)
{
bool fDelete = false;
TRY_CRITICAL_BLOCK(pnode->cs_vSend)
TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
TRY_CRITICAL_BLOCK(pnode->cs_mapRequests)
TRY_CRITICAL_BLOCK(pnode->cs_inventory)
fDelete = true;
if (fDelete)
{
vNodesDisconnected.remove(pnode);
delete pnode;
}
}
}
}
if (vNodes.size() != nPrevNodeCount)
{
nPrevNodeCount = vNodes.size(); // 記錄前一次節點對應的數量
MainFrameRepaint();
}
// 找出哪一個socket有數據要發送
// Find which sockets have data to receive
//
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000; // frequency to poll pnode->vSend 諮詢節點是否有數據要發送的頻率
struct fd_set fdsetRecv; // 記錄所有節點對應的socket句柄和監聽socket句柄
struct fd_set fdsetSend; // 記錄所有有待發送消息的節點對應的socket句柄
FD_ZERO(&fdsetRecv);
FD_ZERO(&fdsetSend);
SOCKET hSocketMax = 0;
FD_SET(hListenSocket, &fdsetRecv); // FD_SET將hListenSocket 放入fdsetRecv對應的數組的最後
hSocketMax = max(hSocketMax, hListenSocket);
CRITICAL_BLOCK(cs_vNodes)
{
foreach(CNode* pnode, vNodes)
{
FD_SET(pnode->hSocket, &fdsetRecv);
hSocketMax = max(hSocketMax, pnode->hSocket); // 找出所有節點對應的socket的最大值,包括監聽socket
TRY_CRITICAL_BLOCK(pnode->cs_vSend)
if (!pnode->vSend.empty())
FD_SET(pnode->hSocket, &fdsetSend); // FD_SET 字段設置
}
}
vfThreadRunning[0] = false;
// 函數參考:https://blog.csdn.net/rootusers/article/details/43604729
/*確定一個或多個套接口的狀態,本函數用於確定一個或多個套接口的狀態,對每一個套接口,調用者可查詢它的可讀性、可寫性及錯誤狀態信息,用fd_set結構來表示一組等待檢查的套接口,在調用返回時,這個結構存有滿足一定條件的套接口組的子集,並且select()返回滿足條件的套接口的數目。
簡單來說select用來填充一組可用的socket句柄,當滿足下列之一條件時:
1.可以讀取的sockets。當這些socket被返回時,在這些socket上執行recv/accept等操作不會產生阻塞;
2.可以寫入的sockets。當這些socket被返回時,在這些socket上執行send等不會產生阻塞;
3.返回有錯誤的sockets。
select()的機制中提供一fd_set的數據結構,實際上市一long類型的數組,每一個數組元素都能與一打開的文件句柄(或者是其他的socket句柄,文件命名管道等)建立聯繫,建立聯繫的工作實際上由程序員完成,當調用select()的時候,由內核根據IO狀態修改fd_set的內容,由此來通知執行了select()的進程那一socket或文件可讀。
*/
int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout);
vfThreadRunning[0] = true;
CheckForShutdown(0);
if (nSelect == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
printf("select failed: %d\n", nErr);
for (int i = 0; i <= hSocketMax; i++)
{
FD_SET(i, &fdsetRecv); // 所有的值設置一遍
FD_SET(i, &fdsetSend);
}
Sleep(timeout.tv_usec/1000);
}
// 隨機增加種子:性能計數
RandAddSeed();
//// debug print
//foreach(CNode* pnode, vNodes)
//{
// printf("vRecv = %-5d ", pnode->vRecv.size());
// printf("vSend = %-5d ", pnode->vSend.size());
//}
//printf("\n");
// 如果fdsetRecv中有監聽socket,則接收改監聽socket對應的鏈接請求,並將鏈接請求設置爲新的節點
// Accept new connections
// 判斷髮送緩衝區中是否有對應的socket,如果有則接收新的交易
if (FD_ISSET(hListenSocket, &fdsetRecv))
{
struct sockaddr_in sockaddr;
int len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); // 接收socket鏈接
CAddress addr(sockaddr);
if (hSocket == INVALID_SOCKET)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
printf("ERROR ThreadSocketHandler accept failed: %d\n", WSAGetLastError());
}
else
{
printf("accepted connection from %s\n", addr.ToString().c_str());
CNode* pnode = new CNode(hSocket, addr, true); // 有新的socket鏈接,則新建對應的節點,並將節點在加入本地節點列表中
pnode->AddRef();
CRITICAL_BLOCK(cs_vNodes)
vNodes.push_back(pnode);
}
}
// 對每一個socket進行服務處理
// Service each socket
//
vector<CNode*> vNodesCopy;
CRITICAL_BLOCK(cs_vNodes)
vNodesCopy = vNodes;
foreach(CNode* pnode, vNodesCopy)
{
CheckForShutdown(0);
SOCKET hSocket = pnode->hSocket; // 獲取每一個節點對應的socket
// 從節點對應的socket中讀取對應的數據,將數據放入節點的接收緩衝區中
// Receive
//
if (FD_ISSET(hSocket, &fdsetRecv))
{
TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
{
CDataStream& vRecv = pnode->vRecv;
unsigned int nPos = vRecv.size();
// typical socket buffer is 8K-64K
const unsigned int nBufSize = 0x10000;
vRecv.resize(nPos + nBufSize);// 調整接收緩衝區的大小
int nBytes = recv(hSocket, &vRecv[nPos], nBufSize, 0);// 從socket中接收對應的數據
vRecv.resize(nPos + max(nBytes, 0));
if (nBytes == 0)
{
// socket closed gracefully (socket優雅的關閉)
if (!pnode->fDisconnect)
printf("recv: socket closed\n");
pnode->fDisconnect = true;
}
else if (nBytes < 0)
{
// socket error
int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
{
if (!pnode->fDisconnect)
printf("recv failed: %d\n", nErr);
pnode->fDisconnect = true;
}
}
}
}
// 將節點對應的發送緩衝中的內容發送出去
// Send
//
if (FD_ISSET(hSocket, &fdsetSend))
{
TRY_CRITICAL_BLOCK(pnode->cs_vSend)
{
CDataStream& vSend = pnode->vSend;
if (!vSend.empty())
{
int nBytes = send(hSocket, &vSend[0], vSend.size(), 0); // 從節點對應的發送緩衝區中發送數據出去
if (nBytes > 0)
{
vSend.erase(vSend.begin(), vSend.begin() + nBytes);// 從發送緩衝區中移除發送過的內容
}
else if (nBytes == 0)
{
if (pnode->ReadyToDisconnect())
pnode->vSend.clear();
}
else
{
printf("send error %d\n", nBytes);
if (pnode->ReadyToDisconnect())
pnode->vSend.clear();
}
}
}
}
}
Sleep(10);
}
}
3.4.3線程ThreadMessageHandler
線程ThreadMessageHandler對應的處理流程:
源碼如下:
// 消息處理線程
void ThreadMessageHandler2(void* parg)
{
printf("ThreadMessageHandler started\n");
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
loop
{
// 輪詢鏈接的節點用於消息處理
// Poll the connected nodes for messages
vector<CNode*> vNodesCopy;
CRITICAL_BLOCK(cs_vNodes)
vNodesCopy = vNodes;
// 對每一個節點進行消息處理:發送消息和接收消息
foreach(CNode* pnode, vNodesCopy)
{
pnode->AddRef();
// Receive messages
TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
ProcessMessages(pnode);
// Send messages
TRY_CRITICAL_BLOCK(pnode->cs_vSend)
SendMessages(pnode);
pnode->Release();
}
// Wait and allow messages to bunch up
vfThreadRunning[2] = false;
Sleep(100);
vfThreadRunning[2] = true;
CheckForShutdown(2);
}
}
// 處理單個節點對應的消息:單個節點接收到的消息進行處理
bool ProcessMessages(CNode* pfrom)
{
CDataStream& vRecv = pfrom->vRecv;
if (vRecv.empty())
return true;
printf("ProcessMessages(%d bytes)\n", vRecv.size());
// 同一個的消息格式
// Message format
// (4) message start
// (12) command
// (4) size
// (x) data
//
// 消息頭包含:message start;command;size;
loop
{
// Scan for message start
CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
// 刪除無效的消息: 就是在對應的消息開始前面還有一些信息
if (vRecv.end() - pstart < sizeof(CMessageHeader))
{
if (vRecv.size() > sizeof(CMessageHeader))
{
printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n");
vRecv.erase(vRecv.begin(), vRecv.end() - sizeof(CMessageHeader));
}
break;
}
if (pstart - vRecv.begin() > 0)
printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin());
vRecv.erase(vRecv.begin(), pstart); // 移除消息開始信息和接收緩衝區開頭之間
// 讀取消息頭
// Read header
CMessageHeader hdr;
vRecv >> hdr; // 指針已經偏移了
if (!hdr.IsValid())
{
printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str());
continue;
}
string strCommand = hdr.GetCommand();
// Message size
unsigned int nMessageSize = hdr.nMessageSize;
if (nMessageSize > vRecv.size())
{
// Rewind and wait for rest of message
///// need a mechanism to give up waiting for overlong message size error
printf("MESSAGE-BREAK 2\n");
vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr));
Sleep(100);
break;
}
// Copy message to its own buffer
CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);
vRecv.ignore(nMessageSize);
// Process message
bool fRet = false;
try
{
CheckForShutdown(2);
CRITICAL_BLOCK(cs_main)
// 根據命令和消息內容進行消息處理
fRet = ProcessMessage(pfrom, strCommand, vMsg);
CheckForShutdown(2);
}
CATCH_PRINT_EXCEPTION("ProcessMessage()")
if (!fRet)
printf("ProcessMessage(%s, %d bytes) from %s to %s FAILED\n", strCommand.c_str(), nMessageSize, pfrom->addr.ToString().c_str(), addrLocalHost.ToString().c_str());
}
vRecv.Compact();
return true;
}
// 對節點pFrom處理命令strCommand對應的消息內容爲vRecv
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
static map<unsigned int, vector<unsigned char> > mapReuseKey;
printf("received: %-12s (%d bytes) ", strCommand.c_str(), vRecv.size());
// 僅僅輸出前25個字符
for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++)
printf("%02x ", vRecv[i] & 0xff);
printf("\n");
// 消息採集頻率進行處理
if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0)
{
printf("dropmessages DROPPING RECV MESSAGE\n");
return true;
}
// 如果命令是版本:節點對應的版本
if (strCommand == "version")
{
// 節點對應的版本只能更新一次,初始爲0,後面進行更新
// Can only do this once
if (pfrom->nVersion != 0)
return false;
int64 nTime;
CAddress addrMe; // 讀取消息對應的內容
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
if (pfrom->nVersion == 0)
return false;
// 更新發送和接收緩衝區中的對應的版本
pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION));
pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
// 如果節點對應的服務類型是節點網絡,則對應節點的客戶端標記就是false
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
if (pfrom->fClient)
{
// 如果不是節點網絡,可能僅僅是一些節點不要保存對應的完整區塊信息,僅僅需要區塊的頭部進行校驗就可以了
pfrom->vSend.nType |= SER_BLOCKHEADERONLY;
pfrom->vRecv.nType |= SER_BLOCKHEADERONLY;
}
// 增加時間樣本數據:沒有什麼用處,僅僅用於輸出
AddTimeData(pfrom->addr.ip, nTime);
// 對第一個進來的節點請求block信息
// Ask the first connected node for block updates
static bool fAskedForBlocks;
if (!fAskedForBlocks && !pfrom->fClient)
{
fAskedForBlocks = true;
pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0));
}
printf("version addrMe = %s\n", addrMe.ToString().c_str());
}
else if (pfrom->nVersion == 0)
{
// 節點在處理任何消息之前一定有一個版本消息
// Must have a version message before anything else
return false;
}
// 地址消息
else if (strCommand == "addr")
{
vector<CAddress> vAddr;
vRecv >> vAddr;
// Store the new addresses
CAddrDB addrdb;
foreach(const CAddress& addr, vAddr)
{
if (fShutdown)
return true;
// 將地址增加到數據庫中
if (AddAddress(addrdb, addr))
{
// Put on lists to send to other nodes
pfrom->setAddrKnown.insert(addr); // 將對應的地址插入到已知地址集合中
CRITICAL_BLOCK(cs_vNodes)
foreach(CNode* pnode, vNodes)
if (!pnode->setAddrKnown.count(addr))
pnode->vAddrToSend.push_back(addr);// 地址的廣播
}
}
}
// 庫存消息
else if (strCommand == "inv")
{
vector<CInv> vInv;
vRecv >> vInv;
CTxDB txdb("r");
foreach(const CInv& inv, vInv)
{
if (fShutdown)
return true;
pfrom->AddInventoryKnown(inv); // 將對應的庫存發送消息增加到庫存發送已知中
bool fAlreadyHave = AlreadyHave(txdb, inv);
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
if (!fAlreadyHave)
pfrom->AskFor(inv);// 如果不存在,則請求諮詢,這裏會在線程中發送getdata消息
else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(mapOrphanBlocks[inv.hash]));
}
}
// 獲取數據
else if (strCommand == "getdata")
{
vector<CInv> vInv;
vRecv >> vInv;
foreach(const CInv& inv, vInv)
{
if (fShutdown)
return true;
printf("received getdata for: %s\n", inv.ToString().c_str());
if (inv.type == MSG_BLOCK)
{
// Send block from disk
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
if (mi != mapBlockIndex.end())
{
//// could optimize this to send header straight from blockindex for client
CBlock block;
block.ReadFromDisk((*mi).second, !pfrom->fClient);
pfrom->PushMessage("block", block);// 獲取數據對應的類型是block,則發送對應的塊信息
}
}
else if (inv.IsKnownType())
{
// Send stream from relay memory
CRITICAL_BLOCK(cs_mapRelay)
{
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv); // 重新轉播的內容
if (mi != mapRelay.end())
pfrom->PushMessage(inv.GetCommand(), (*mi).second);
}
}
}
}
else if (strCommand == "getblocks")
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
//找到本地有的且在主鏈上的
// Find the first block the caller has in the main chain
CBlockIndex* pindex = locator.GetBlockIndex();
// 將匹配得到的塊索引之後的所有在主鏈上的塊發送出去
// Send the rest of the chain
if (pindex)
pindex = pindex->pnext;
printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,14).c_str());
for (; pindex; pindex = pindex->pnext)
{
if (pindex->GetBlockHash() == hashStop)
{
printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str());
break;
}
// Bypass setInventoryKnown in case an inventory message got lost
CRITICAL_BLOCK(pfrom->cs_inventory)
{
CInv inv(MSG_BLOCK, pindex->GetBlockHash());
// 判斷在已知庫存2中是否存在
// returns true if wasn't already contained in the set
if (pfrom->setInventoryKnown2.insert(inv).second)
{
pfrom->setInventoryKnown.erase(inv);
pfrom->vInventoryToSend.push_back(inv);// 插入對應的庫存發送集合中準備發送,在另一個線程中進行發送,發送的消息爲inv
}
}
}
}
// 交易命令
else if (strCommand == "tx")
{
vector<uint256> vWorkQueue;
CDataStream vMsg(vRecv);
CTransaction tx;
vRecv >> tx;
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);// 將交易消息放入到對應的已知庫存中
bool fMissingInputs = false;
// 如果交易能夠被接受
if (tx.AcceptTransaction(true, &fMissingInputs))
{
AddToWalletIfMine(tx, NULL);
RelayMessage(inv, vMsg);// 轉播消息
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
// 遞歸處理所有依賴這個交易對應的孤兒交易
// Recursively process any orphan transactions that depended on this one
for (int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (multimap<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev);
mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev);
++mi)
{
const CDataStream& vMsg = *((*mi).second);
CTransaction tx;
CDataStream(vMsg) >> tx;
CInv inv(MSG_TX, tx.GetHash());
if (tx.AcceptTransaction(true))
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str());
AddToWalletIfMine(tx, NULL);
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
}
}
}
foreach(uint256 hash, vWorkQueue)
EraseOrphanTx(hash);
}
else if (fMissingInputs)
{
printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str());
AddOrphanTx(vMsg); // 如果交易當前不被接受則對應的孤兒交易
}
}
else if (strCommand == "review")
{
CDataStream vMsg(vRecv);
CReview review;
vRecv >> review;
CInv inv(MSG_REVIEW, review.GetHash());
pfrom->AddInventoryKnown(inv);
if (review.AcceptReview())
{
// Relay the original message as-is in case it's a higher version than we know how to parse
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
}
}
else if (strCommand == "block")
{
auto_ptr<CBlock> pblock(new CBlock);
vRecv >> *pblock;
//// debug print
printf("received block:\n"); pblock->print();
CInv inv(MSG_BLOCK, pblock->GetHash());
pfrom->AddInventoryKnown(inv);// 增加庫存
if (ProcessBlock(pfrom, pblock.release()))
mapAlreadyAskedFor.erase(inv);
}
else if (strCommand == "getaddr")
{
pfrom->vAddrToSend.clear();
//// need to expand the time range if not enough found
int64 nSince = GetAdjustedTime() - 60 * 60; // in the last hour 往前推一個小時
CRITICAL_BLOCK(cs_mapAddresses)
{
foreach(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
{
if (fShutdown)
return true;
const CAddress& addr = item.second;
if (addr.nTime > nSince)
pfrom->vAddrToSend.push_back(addr);
}
}
}
else if (strCommand == "checkorder")
{
uint256 hashReply;
CWalletTx order;
vRecv >> hashReply >> order;
/// we have a chance to check the order here
// Keep giving the same key to the same ip until they use it
if (!mapReuseKey.count(pfrom->addr.ip))
mapReuseKey[pfrom->addr.ip] = GenerateNewKey();
// Send back approval of order and pubkey to use
CScript scriptPubKey;
scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG;
pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey);
}
else if (strCommand == "submitorder")
{
uint256 hashReply;
CWalletTx wtxNew;
vRecv >> hashReply >> wtxNew;
// Broadcast
if (!wtxNew.AcceptWalletTransaction())
{
pfrom->PushMessage("reply", hashReply, (int)1);
return error("submitorder AcceptWalletTransaction() failed, returning error 1");
}
wtxNew.fTimeReceivedIsTxTime = true;
AddToWallet(wtxNew);
wtxNew.RelayWalletTransaction();
mapReuseKey.erase(pfrom->addr.ip);
// Send back confirmation
pfrom->PushMessage("reply", hashReply, (int)0);
}
else if (strCommand == "reply")
{
uint256 hashReply;
vRecv >> hashReply;
CRequestTracker tracker;
CRITICAL_BLOCK(pfrom->cs_mapRequests)
{
map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply);
if (mi != pfrom->mapRequests.end())
{
tracker = (*mi).second;
pfrom->mapRequests.erase(mi);
}
}
if (!tracker.IsNull())
tracker.fn(tracker.param1, vRecv);
}
else
{
// Ignore unknown commands for extensibility
printf("ProcessMessage(%s) : Ignored unknown message\n", strCommand.c_str());
}
if (!vRecv.empty())
printf("ProcessMessage(%s) : %d extra bytes\n", strCommand.c_str(), vRecv.size());
return true;
}
// 處理節點對應的消息發送
bool SendMessages(CNode* pto)
{
CheckForShutdown(2);
CRITICAL_BLOCK(cs_main)
{
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return true;
// 消息發送的地址
// Message: addr
//
vector<CAddress> vAddrToSend;
vAddrToSend.reserve(pto->vAddrToSend.size());
// 如果發送的地址不在已知地址的集合中,則將其放入臨時地址發送數組中
foreach(const CAddress& addr, pto->vAddrToSend)
if (!pto->setAddrKnown.count(addr))
vAddrToSend.push_back(addr);
// 清空地址發送的數組
pto->vAddrToSend.clear();
// 如果臨時地址發送數組不爲空,則進行地址的消息的發送
if (!vAddrToSend.empty())
pto->PushMessage("addr", vAddrToSend);
// 庫存消息處理
// Message: inventory
//
vector<CInv> vInventoryToSend;
CRITICAL_BLOCK(pto->cs_inventory)
{
vInventoryToSend.reserve(pto->vInventoryToSend.size());
foreach(const CInv& inv, pto->vInventoryToSend)
{
// returns true if wasn't already contained in the set
if (pto->setInventoryKnown.insert(inv).second)
vInventoryToSend.push_back(inv);
}
pto->vInventoryToSend.clear();
pto->setInventoryKnown2.clear();
}
// 庫存消息發送
if (!vInventoryToSend.empty())
pto->PushMessage("inv", vInventoryToSend);
// getdata消息發送
// Message: getdata
//
vector<CInv> vAskFor;
int64 nNow = GetTime() * 1000000;
CTxDB txdb("r");
// 判斷節點對應的請求消息map是否爲空,且對應的請求map中的消息對應的最早請求時間是否小於當前時間
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
printf("sending getdata: %s\n", inv.ToString().c_str());
if (!AlreadyHave(txdb, inv))
vAskFor.push_back(inv);// 不存在才需要進行消息發送
pto->mapAskFor.erase(pto->mapAskFor.begin());// 請求消息處理完一條就刪除一條
}
if (!vAskFor.empty())
pto->PushMessage("getdata", vAskFor);
}
return true;
}
// 處理節點對應的消息發送
bool SendMessages(CNode* pto)
{
CheckForShutdown(2);
CRITICAL_BLOCK(cs_main)
{
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return true;
// 消息發送的地址
// Message: addr
//
vector<CAddress> vAddrToSend;
vAddrToSend.reserve(pto->vAddrToSend.size());
// 如果發送的地址不在已知地址的集合中,則將其放入臨時地址發送數組中
foreach(const CAddress& addr, pto->vAddrToSend)
if (!pto->setAddrKnown.count(addr))
vAddrToSend.push_back(addr);
// 清空地址發送的數組
pto->vAddrToSend.clear();
// 如果臨時地址發送數組不爲空,則進行地址的消息的發送
if (!vAddrToSend.empty())
pto->PushMessage("addr", vAddrToSend);
// 庫存消息處理
// Message: inventory
//
vector<CInv> vInventoryToSend;
CRITICAL_BLOCK(pto->cs_inventory)
{
vInventoryToSend.reserve(pto->vInventoryToSend.size());
foreach(const CInv& inv, pto->vInventoryToSend)
{
// returns true if wasn't already contained in the set
if (pto->setInventoryKnown.insert(inv).second)
vInventoryToSend.push_back(inv);
}
pto->vInventoryToSend.clear();
pto->setInventoryKnown2.clear();
}
// 庫存消息發送
if (!vInventoryToSend.empty())
pto->PushMessage("inv", vInventoryToSend);
// getdata消息發送
// Message: getdata
//
vector<CInv> vAskFor;
int64 nNow = GetTime() * 1000000;
CTxDB txdb("r");
// 判斷節點對應的請求消息map是否爲空,且對應的請求map中的消息對應的最早請求時間是否小於當前時間
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
printf("sending getdata: %s\n", inv.ToString().c_str());
if (!AlreadyHave(txdb, inv))
vAskFor.push_back(inv);// 不存在才需要進行消息發送
pto->mapAskFor.erase(pto->mapAskFor.begin());// 請求消息處理完一條就刪除一條
}
if (!vAskFor.empty())
pto->PushMessage("getdata", vAskFor);
}
return true;
}
3.5節點不同緩衝區
節點對應不同緩衝區的處理關係如下圖所示:
3.6挖礦線程ThreadBitcoinMiner處理流程
挖礦處理流程如下圖所示:
源碼內容如下:
// 節點挖礦
bool BitcoinMiner()
{
printf("BitcoinMiner started\n");
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
CKey key;
key.MakeNewKey(); // 使用橢圓曲線算法獲得一對公鑰和私鑰
// 隨機數從0開始
CBigNum bnExtraNonce = 0;
while (fGenerateBitcoins)
{
Sleep(50);
CheckForShutdown(3);
while (vNodes.empty())
{
Sleep(1000);
CheckForShutdown(3);
}
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
// 獲取挖礦難度
unsigned int nBits = GetNextWorkRequired(pindexPrev);
// 創建幣基交易
// Create coinbase tx
//
CTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vin[0].scriptSig << nBits << ++bnExtraNonce;
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG;
// 創建新的區塊
// Create new block
//
auto_ptr<CBlock> pblock(new CBlock());
if (!pblock.get())
return false;
// 增加幣基交易左右區塊的第一個交易
// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);
// 收集最新的交易放入區塊中
// Collect the latest transactions into the block
int64 nFees = 0;
CRITICAL_BLOCK(cs_main)
CRITICAL_BLOCK(cs_mapTransactions)
{
CTxDB txdb("r");
map<uint256, CTxIndex> mapTestPool;
vector<char> vfAlreadyAdded(mapTransactions.size());
bool fFoundSomething = true;
unsigned int nBlockSize = 0;
// 外層循環是因爲是多線程,可能剛開始對應的交易沒有怎麼多,則在等待交易,進行打包,只等待一輪,如果mapTransactions有很多交易則一起打包
while (fFoundSomething && nBlockSize < MAX_SIZE/2)
{
fFoundSomething = false;
unsigned int n = 0;
for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi, ++n)
{
if (vfAlreadyAdded[n])
continue;
CTransaction& tx = (*mi).second;
if (tx.IsCoinBase() || !tx.IsFinal())
continue;
// Transaction fee requirements, mainly only needed for flood control
// Under 10K (about 80 inputs) is free for first 100 transactions
// Base rate is 0.01 per KB
// 根據費用來判斷每一個交易需要的最少費用
int64 nMinFee = tx.GetMinFee(pblock->vtx.size() < 100);
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
// 判斷當前交易是否滿足對應的最低費用要求,對應的nFees在ConnectInputs是進行累加的
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), 0, nFees, false, true, nMinFee))
continue;
swap(mapTestPool, mapTestPoolTmp);
pblock->vtx.push_back(tx);
nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); // 將當前加入塊的交易大小加入對應的塊大小中
vfAlreadyAdded[n] = true;
fFoundSomething = true;
}
}
}
pblock->nBits = nBits; // 設置對應的挖坑難度值
pblock->vtx[0].vout[0].nValue = pblock->GetBlockValue(nFees); // 設置對應的塊第一個交易對應的輸出對應的值=獎勵 + 交易費用
printf("\n\nRunning BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
//
// Prebuild hash buffer
//
struct unnamed1
{
struct unnamed2
{
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
unsigned int nTime;
unsigned int nBits;
unsigned int nNonce;
}
block;
unsigned char pchPadding0[64];
uint256 hash1;
unsigned char pchPadding1[64];
}
tmp;
tmp.block.nVersion = pblock->nVersion;
tmp.block.hashPrevBlock = pblock->hashPrevBlock = (pindexPrev ? pindexPrev->GetBlockHash() : 0);
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot = pblock->BuildMerkleTree();
// 取前11個區塊對應的創建時間對應的中位數
tmp.block.nTime = pblock->nTime = max((pindexPrev ? pindexPrev->GetMedianTimePast()+1 : 0), GetAdjustedTime());
tmp.block.nBits = pblock->nBits = nBits;
tmp.block.nNonce = pblock->nNonce = 1; // 隨機數從1開始
unsigned int nBlocks0 = FormatHashBlocks(&tmp.block, sizeof(tmp.block));
unsigned int nBlocks1 = FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
//
// Search
//
unsigned int nStart = GetTime();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); // 根據難度係數值獲取對應的hash目標值
uint256 hash;
loop
{
BlockSHA256(&tmp.block, nBlocks0, &tmp.hash1);
BlockSHA256(&tmp.hash1, nBlocks1, &hash);
// 挖礦成功
if (hash <= hashTarget)
{
pblock->nNonce = tmp.block.nNonce;
assert(hash == pblock->GetHash());
//// debug print
printf("BitcoinMiner:\n");
printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
pblock->print();
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
CRITICAL_BLOCK(cs_main)
{
// Save key
if (!AddKey(key))
return false;
key.MakeNewKey();
// Process this block the same as if we had received it from another node
if (!ProcessBlock(NULL, pblock.release()))
printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n");
}
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
Sleep(500);
break;
}
// 更新區塊創建時間,重新用於挖礦
// Update nTime every few seconds
if ((++tmp.block.nNonce & 0x3ffff) == 0)
{
CheckForShutdown(3);
if (tmp.block.nNonce == 0)
break;
if (pindexPrev != pindexBest)
break;
if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
if (!fGenerateBitcoins)
break;
tmp.block.nTime = pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
}
}
}
return true;
}
4. 源碼地址
我對比特幣bitcoin-0.1.0源碼加了詳細的註釋,對應的下載地址:https://github.com/lwjaiyjk/bitcoin-comment-0.1.0.git