挖矿函数BitcoinMiner()详解
CTransaction txNew;
const char* pszTimestamp = "This is the second block,which have two transactions";
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vin[0].scriptSig << nBits << ++bnExtraNonce << vector((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG;
txNew.print();
运行结果:
//
// 创建新区块
//
auto_ptr pblock(new CBlock());
if (!pblock.get())
return false;
// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);
7.1.3.1 遍历交易容器mapTransactions
for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi, ++n)
{
……
}
7.1.3.2 设置手续费为0;读取容器中的交易
int64 nMinFee = 0;
CTransaction& tx = (*mi).second;
7.1.3.3 交易写入区块;区块大小增加
pblock->vtx.push_back(tx);
nBlockSize += ::GetSerializeSize(tx, SER_NETWORK);
printf("\nTransaction Info is:\n");
for (int i = 0; i < pblock->vtx.size(); i++)
{
printf(" ");
pblock->vtx[i].print();
}
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;
CBlockIndex* pindexPrev = pindexBest;
unsigned int nBits = GetNextWorkRequired(pindexPrev);
tmp.block.nVersion = pblock->nVersion;
tmp.block.hashPrevBlock = pblock->hashPrevBlock = (pindexPrev ? pindexPrev->GetBlockHash() : 0);
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot = pblock->BuildMerkleTree();
tmp.block.nTime = pblock->nTime = max((pindexPrev ? pindexPrev->GetMedianTimePast()+1 : 0), GetAdjustedTime());
tmp.block.nBits = pblock->nBits = nBits;
tmp.block.nNonce = pblock->nNonce = 1;
区块头赋值详解
tmp.block.nVersion = pblock->nVersion;
实例化区块对象时,区块类CBlock的构造函数调用SetNull()函数初始化数据。
//区块类
CBlock()
{
SetNull();
}
//区块数据初始化函数:SetNull()
void SetNull()
{
nVersion = 1;
hashPrevBlock = 0;
hashMerkleRoot = 0;
nTime = 0;
nBits = 0;
nNonce = 0;
vtx.clear();
vMerkleTree.clear();
}
tmp.block.hashPrevBlock = pblock->hashPrevBlock = (pindexPrev ? pindexPrev->GetBlockHash() : 0);
如果当前区块不存在前一区块索引对象,则当前区块为创世区块,前一区块哈希值为0;否则,调用前一区块索引对象的GetBlockHash()函数,赋值给当前区块前一区块哈希值属性。
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot = pblock->BuildMerkleTree();
根据收集的交易创建默克尔树,赋值默克尔根。
tmp.block.nTime = pblock->nTime = max((pindexPrev ? pindexPrev->GetMedianTimePast()+1 : 0), GetAdjustedTime());
如果当前区块不存在前一区块索引对象,则当前区块为创世区块,时间戳GetAdjustedTime()函数获取;否则,调用前一区块索引对象的GetMedianTimePast()函数得到时间戳,赋值给当前区块时间戳属性。
int64 GetTime()
{
return time(NULL);
}
static int64 nTimeOffset = 0;
int64 GetAdjustedTime()
{
return GetTime() + nTimeOffset;
}
tmp.block.nBits = pblock->nBits = nBits;
将之前得到的目标值复制带区块目标值属性。
tmp.block.nNonce = pblock->nNonce = 1;
初始化随机数为1,随着挖矿过程的进行随机数将逐渐增加。
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
cout << "\nReal hashTarget=" << hashTarget.ToString().substr(0,14).c_str() << endl;
BlockSHA256(&tmp.block, nBlocks0, &tmp.hash1);
BlockSHA256(&tmp.hash1, nBlocks1, &hash);
//输出本次挖矿结果
cout << "\nnNonce=" << tmp.block.nNonce << endl << "hash=" << hash.ToString().substr(0,14).c_str() << endl;
if (hash <= newhashTarget)
{
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(), newhashTarget.GetHex().c_str());
pblock->print();
}
当计算出来的区块哈希值小于难度目标,挖矿成功。将这个符合条件的随机数nNonce赋值区块随机数属性,完成PoW机制巡展随机数过程。
if ((++tmp.block.nNonce & 0x3ffff) == 0)
{
if (tmp.block.nNonce == 0)
break;
if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
if (!fGenerateBitcoins)
break;
tmp.block.nTime = pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
}
当计算出来的区块哈希值大于难度目标,本次挖矿失败。nNonce值加1,继续挖矿过程。更新区块头时间戳属性。
本章主要描述矿工挖到区块或者收到“block”消息后进行的区块处理。主要包含区块有效性检查,孤立区块处理以及当前区块处理等。
流程图如下所示:
本文主要描述bool ProcessBlock(CNode pfrom, CBlock pblock)函数的功能。
Ø 区块重复性检查:
ü 区块是否在区块链中,如果在则返回失败
uint256 hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AcceptBlock() : block already in mapBlockIndex");
ü 区块是否在孤立区块中,如果在则返回失败
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,14).c_str());
Ø 区块基本检查
if (!pblock->CheckBlock())
{
delete pblock;
return error("ProcessBlock() : CheckBlock FAILED");
}
ü 检查区块大小、时间戳
// Size limits
if (vtx.empty() || vtx.size() > MAX_SIZE || ::GetSerializeSize(*this, SER_DISK) > MAX_SIZE)
return error("CheckBlock() : size limits failed");
// Check timestamp
if (nTime > GetAdjustedTime() + 2 * 60 * 60)
return error("CheckBlock() : block timestamp too far in the future");
ü 检查交易、PoW和MerkleRoot
// First transaction must be coinbase, the rest must not be
if (vtx.empty() || !vtx[0].IsCoinBase())
return error("CheckBlock() : first tx is not coinbase");
for (int i = 1; i < vtx.size(); i++)
if (vtx[i].IsCoinBase())
return error("CheckBlock() : more than one coinbase");
// Check transactions
foreach(const CTransaction& tx, vtx)
if (!tx.CheckTransaction())
return error("CheckBlock() : CheckTransaction failed");
// Check proof of work matches claimed amount
if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit)
return error("CheckBlock() : nBits below minimum work");
if (GetHash() > CBigNum().SetCompact(nBits).getuint256())
return error("CheckBlock() : hash doesn't match nBits");
// Check merkleroot
if (hashMerkleRoot != BuildMerkleTree())
return error("CheckBlock() : hashMerkleRoot mismatch");
Ø 根据区块前一区块进行处理
如果前一区块不存在区块链中,则将前一区块加入孤立区块
此种情况,表示区块链出现分叉或者网络延迟导致的,则需要发送“getblocks
获取最长链中缺少的区块信息。
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,14).c_str());
mapOrphanBlocks.insert(make_pair(hash, pblock));
mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
// Ask this guy to fill in what we're missing
if (pfrom)
pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(pblock));
return true;
}
Ø 如果前一区块不存在区块链中,则尝试接受区块
if (!pblock->AcceptBlock())
{
delete pblock;
return error("ProcessBlock() : AcceptBlock FAILED");
}
delete pblock;
ü 接受区块:“接受区块”包含区块重复性检查,前一区块有效性区块,时间戳检查,工作量证明;当这些条件检查通过后,将区块写入磁盘并加入到区块链中。
bool CBlock::AcceptBlock()
{
// Check for duplicate
uint256 hash = GetHash();
if (mapBlockIndex.count(hash)) // 当前区块重复性检查
return error("AcceptBlock() : block already in mapBlockIndex");
// Get prev block index
map::iterator mi = mapBlockIndex.find(hashPrevBlock);
if (mi == mapBlockIndex.end()) // 前一区块有效性检查
return error("AcceptBlock() : prev block not found");
CBlockIndex* pindexPrev = (*mi).second;
// Check timestamp against prev
if (nTime <= pindexPrev->GetMedianTimePast()) // 时间戳检查
return error("AcceptBlock() : block's timestamp is too early");
// Check proof of work
if (nBits != GetNextWorkRequired(pindexPrev)) // 工作量检查
return error("AcceptBlock() : incorrect proof of work");
// Write block to history file
unsigned int nFile;
unsigned int nBlockPos;
if (!WriteToDisk(!fClient, nFile, nBlockPos)) // 区块写入磁盘中
return error("AcceptBlock() : WriteToDisk failed");
if (!AddToBlockIndex(nFile, nBlockPos)) // 加入到区块链中
return error("AcceptBlock() : AddToBlockIndex failed");
if (hashBestChain == hash) // 如果该区块链式最长链,则广播消息
RelayInventory(CInv(MSG_BLOCK, hash));
return true;
}
ü 处理孤立区块和当前区块的关系,并尝试接受孤立区块
vector vWorkQueue;
vWorkQueue.push_back(hash);
for (int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
mi != mapOrphanBlocksByPrev.upper_bound(hashPrev);
++mi)
{
CBlock* pblockOrphan = (*mi).second;
if (pblockOrphan->AcceptBlock()) // 尝试接受孤立区块
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
delete pblockOrphan;
}
mapOrphanBlocksByPrev.erase(hashPrev);
}
本文主要描述区块确认通过之后,如何将该区块加到最长的区块链中(描述AddToBlockIndex函数的功能),主要描述如何选取网络中的最长链,如何更新区块到最长链中。
流程图如下所示:
Ø 区块检查
ü 当前区块如果存在已有区块链中,则返回错误
uint256 hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,14).c_str());
Ø 新建区块索引
CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
map::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
Ø 保存区块索引
CTxDB txdb;
txdb.TxnBegin();
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
Ø 当前区块链高度大于网络中最长区块高度
if (pindexNew->nHeight > nBestHeight)
{
...
}
ü 当前区块是创世块则直接更新到最长区块链
if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
{
pindexGenesisBlock = pindexNew;
txdb.WriteHashBestChain(hash);
}
ü 前一区块是最长区块链则直接添加当前区块
else if (hashPrevBlock == hashBestChain)
{
// Adding to current best branch
if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
{
txdb.TxnAbort();
pindexNew->EraseBlockFromDisk();
mapBlockIndex.erase(pindexNew->GetBlockHash());
delete pindexNew;
return error("AddToBlockIndex() : ConnectBlock failed");
}
txdb.TxnCommit();
pindexNew->pprev->pnext = pindexNew;
// Delete redundant memory transactions
foreach(CTransaction& tx, vtx)
tx.RemoveFromMemoryPool();
}
ü 前一区块不是最长区块链则进行共识处理
else
{
// New best branch
if (!Reorganize(txdb, pindexNew))
{
txdb.TxnAbort();
return error("AddToBlockIndex() : Reorganize failed");
}
}
① 寻找当前最长链和分支链(网络中最长链)的交点
CBlockIndex* pfork = pindexBest;
CBlockIndex* plonger = pindexNew;
while (pfork != plonger)
{
if (!(pfork = pfork->pprev))
return error("Reorganize() : pfork->pprev is null");
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
return error("Reorganize() : plonger->pprev is null");
}
② 清除分支链的区块信息
vector vDisconnect;
for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev)
vDisconnect.push_back(pindex);
// List of what to connect
vector vConnect;
for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev)
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
// Disconnect shorter branch
vector vResurrect;
foreach(CBlockIndex* pindex, vDisconnect)
{
CBlock block;
if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true))
return error("Reorganize() : ReadFromDisk for disconnect failed");
if (!block.DisconnectBlock(txdb, pindex))
return error("Reorganize() : DisconnectBlock failed");
// Queue memory transactions to resurrect
foreach(const CTransaction& tx, block.vtx)
if (!tx.IsCoinBase())
vResurrect.push_back(tx);
}
③ 将此时最长链对接到网络中最长链
// Connect longer branch
vector vDelete;
for (int i = 0; i < vConnect.size(); i++)
{
CBlockIndex* pindex = vConnect[i];
CBlock block;
if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true))
return error("Reorganize() : ReadFromDisk for connect failed");
if (!block.ConnectBlock(txdb, pindex))
{
// Invalid block, delete the rest of this branch
txdb.TxnAbort();
for (int j = i; j < vConnect.size(); j++)
{
CBlockIndex* pindex = vConnect[j];
pindex->EraseBlockFromDisk();
txdb.EraseBlockIndex(pindex->GetBlockHash());
mapBlockIndex.erase(pindex->GetBlockHash());
delete pindex;
}
return error("Reorganize() : ConnectBlock failed");
}
// Queue memory transactions to delete
foreach(const CTransaction& tx, block.vtx)
vDelete.push_back(tx);
}
④ 更新网络最长链
if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
return error("Reorganize() : WriteHashBestChain failed");
// Commit now because resurrecting could take some time
txdb.TxnCommit();
// Disconnect shorter branch
foreach(CBlockIndex* pindex, vDisconnect)
if (pindex->pprev)
pindex->pprev->pnext = NULL;
// Connect longer branch
foreach(CBlockIndex* pindex, vConnect)
if (pindex->pprev)
pindex->pprev->pnext = pindex;
// Resurrect memory transactions that were in the disconnected branch
foreach(CTransaction& tx, vResurrect)
tx.AcceptTransaction(txdb, false);
// Delete redundant memory transactions that are in the connected branch
foreach(CTransaction& tx, vDelete)
tx.RemoveFromMemoryPool();
Ø 更新对应的全局变量
// New best link
hashBestChain = hash;
pindexBest = pindexNew;
nBestHeight = pindexBest->nHeight;
nTransactionsUpdated++;
printf("AddToBlockIndex: new best=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);
Ø 向P2P网络广播
txdb.TxnCommit();
txdb.Close();
// Relay wallet transactions that haven't gotten in yet
if (pindexNew == pindexBest)
RelayWalletTrans