交易打包是在addPackageTxs函數(shù),在src/miner.cpp里坐梯。在解析它之前票髓,先看一下它用到的重要數(shù)據(jù)結(jié)構(gòu)。
//對(duì)交易池中每個(gè)交易進(jìn)行再打包托启,用于計(jì)算包含祖先交易的費(fèi)率,便于用來(lái)排序
struct CTxMemPoolModifiedEntry {
//顯式構(gòu)造函數(shù)攘宙,參數(shù)只需要一個(gè)txiter屯耸,其實(shí)就是CTxMemPoolEntry
explicit CTxMemPoolModifiedEntry(CTxMemPool::txiter entry)
{
iter = entry;
nSizeWithAncestors = entry->GetSizeWithAncestors();
nModFeesWithAncestors = entry->GetModFeesWithAncestors();
nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors();
}
int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
size_t GetTxSize() const { return iter->GetTxSize(); }
const CTransaction& GetTx() const { return iter->GetTx(); }
CTxMemPool::txiter iter;
uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors;
int64_t nSigOpCostWithAncestors;
};
接下來(lái)是幾個(gè)輔助結(jié)構(gòu):
//第一個(gè)比較運(yùn)算子,只是簡(jiǎn)單地比較內(nèi)存地址蹭劈,用來(lái)作索引
struct CompareCTxMemPoolIter {
bool operator()(const CTxMemPool::txiter& a, const CTxMemPool::txiter& b) const
{
return &(*a) < &(*b);
}
};
//用于提取交易(CTxMemPoolEntry)的運(yùn)算子
struct modifiedentry_iter {
typedef CTxMemPool::txiter result_type;
result_type operator() (const CTxMemPoolModifiedEntry &entry) const
{
return entry.iter;
}
};
//第二個(gè)比較運(yùn)算子疗绣,比較的是包含祖先交易的費(fèi)率(fee/size),如果相等铺韧,就比較交易Hash
//這里的祖先交易是指在交易池里還未被確認(rèn)的
//這個(gè)運(yùn)算子定義在txmempool.h里
class CompareTxMemPoolEntryByAncestorFee
{
public:
template<typename T>
bool operator()(const T& a, const T& b) const
{
double a_mod_fee, a_size, b_mod_fee, b_size;
GetModFeeAndSize(a, a_mod_fee, a_size);
GetModFeeAndSize(b, b_mod_fee, b_size);
//變除為乘多矮,小而管用的運(yùn)算技巧
double f1 = a_mod_fee * b_size;
double f2 = a_size * b_mod_fee;
if (f1 == f2) {
return a.GetTx().GetHash() < b.GetTx().GetHash();
}
return f1 > f2;
}
//獲取費(fèi)率(fee/size),用于排序
template <typename T>
void GetModFeeAndSize(const T &a, double &mod_fee, double &size) const
{
//兩種算法哈打,取其小
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithAncestors();
double f2 = (double)a.GetModFeesWithAncestors() * a.GetTxSize();
if (f1 > f2) {
mod_fee = a.GetModFeesWithAncestors();
size = a.GetSizeWithAncestors();
} else {
mod_fee = a.GetModifiedFee();
size = a.GetTxSize();
}
}
};
OK塔逃,可以引出交易打包使用的多重索引集合:
typedef boost::multi_index_container<
CTxMemPoolModifiedEntry, //集合中的項(xiàng)目類(lèi)型就是CTxMemPoolModifiedEntry
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
modifiedentry_iter, //唯一性索引,基于內(nèi)部的CTxMemPoolEntry
CompareCTxMemPoolIter //地址比較運(yùn)算子
>,
//第二個(gè)索引料仗,非唯一性湾盗,基于包含祖先交易的費(fèi)率
boost::multi_index::ordered_non_unique<
//使用ancestor_score作為索引tag,它是一個(gè)空結(jié)構(gòu)立轧,定義在txmempool.h里
boost::multi_index::tag<ancestor_score>,
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
CompareTxMemPoolEntryByAncestorFee //包含祖先交易的費(fèi)率比較運(yùn)算子
>
>
> indexed_modified_transaction_set;
//下面是兩個(gè)迭代子格粪,分別對(duì)應(yīng)兩個(gè)索引
typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter;
typedef indexed_modified_transaction_set::index<ancestor_score>::type::iterator modtxscoreiter;
最后進(jìn)入今天的主菜躏吊,交易是怎么打包的。先看簡(jiǎn)要流程圖:詳細(xì)解析見(jiàn)以下源碼和注釋?zhuān)?/p>
//交易選擇算法:按照交易費(fèi)率(包含未被確認(rèn)的祖先交易)排序
//擬入塊的交易需要更新其祖先帐萎、子孫相關(guān)信息比伏,又不能影響交易池中原始信息,
//因此需要一個(gè)新的mapModifiedTx來(lái)存儲(chǔ)處理過(guò)的交易信息
//打包過(guò)程中吓肋,不斷從mapModifiedTx和mapTx中抽取費(fèi)率最高的交易,
//選擇其中費(fèi)率更高的進(jìn)行打包瑰艘,并完成相關(guān)處理
void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated)
{
//mapModifiedTx存儲(chǔ)是的待入塊的交易是鬼,按照預(yù)定索引排序
//并根據(jù)其祖先、子孫交易的入塊情況紫新,更新交易關(guān)聯(lián)信息(大小均蜜、費(fèi)率等)
indexed_modified_transaction_set mapModifiedTx;
//入塊失敗的交易存在這里,防止重復(fù)操作
CTxMemPool::setEntries failedTx;
//對(duì)已經(jīng)入塊的交易芒率,其子孫交易加入mapModifiedTx囤耳。
//若子孫交易已入塊則不作處理,否則偶芍,減去已入塊交易的Size充择、FeeRate等
//inBlock是txiter的集合(setEntries),存儲(chǔ)的是所有已入塊的交易
UpdatePackagesForAdded(inBlock, mapModifiedTx);
//mi匪蟀,MempoolItem椎麦,用來(lái)迭代整個(gè)交易池里的所有交易
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = mempool.mapTx.get<ancestor_score>().begin();
CTxMemPool::txiter iter;
//限定區(qū)塊將滿(mǎn)時(shí)添加交易的嘗試次數(shù),確保在交易過(guò)多時(shí)及時(shí)結(jié)束打包過(guò)程
const int64_t MAX_CONSECUTIVE_FAILURES = 1000;
int64_t nConsecutiveFailed = 0;
//按照包含祖先費(fèi)率排序材彪,逐個(gè)迭代交易
while (mi != mempool.mapTx.get<ancestor_score>().end() || !mapModifiedTx.empty())
{
//如果已經(jīng)在mapModifiedTx观挎、inBlock或failedTx中,跳過(guò)去不處理
if (mi != mempool.mapTx.get<ancestor_score>().end() &&
SkipMapTxEntry(mempool.mapTx.project<0>(mi), mapModifiedTx, failedTx)) {
++mi;
continue;
}
//下一個(gè)待處理的交易段化,是從mapTx選呢嘁捷?還是從mapModifiedTx選?
bool fUsingModified = false;
modtxscoreiter modit = mapModifiedTx.get<ancestor_score>().begin();
if (mi == mempool.mapTx.get<ancestor_score>().end()) {
//mapTx已經(jīng)空了显熏,就從mapModifiedTx選
iter = modit->iter;
fUsingModified = true;
} else {
//沒(méi)空的話雄嚣,比較一下mapTx和mapModifiedTx的當(dāng)前項(xiàng)
iter = mempool.mapTx.project<0>(mi);
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
//mapModifiedTx當(dāng)前項(xiàng)費(fèi)率更高,選它了
iter = modit->iter;
fUsingModified = true;
} else {
//mapModifiedTx空了喘蟆,或者沒(méi)有mapTx當(dāng)前項(xiàng)費(fèi)率高现诀,就用mapTx的
++mi;
}
}
//inBlock檢查前面已經(jīng)做過(guò)了,這里的iter肯定不在inBlock里
assert(!inBlock.count(iter));
uint64_t packageSize = iter->GetSizeWithAncestors();
CAmount packageFees = iter->GetModFeesWithAncestors();
int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors();
if (fUsingModified) {
packageSize = modit->nSizeWithAncestors;
packageFees = modit->nModFeesWithAncestors;
packageSigOpsCost = modit->nSigOpCostWithAncestors;
}
if (packageFees < blockMinFeeRate.GetFee(packageSize)) {
//少于最低費(fèi)用履肃,退出
return;
}
if (!TestPackage(packageSize, packageSigOpsCost)) {
if (fUsingModified) {
//TestPackage主要是檢查區(qū)塊大小有沒(méi)有超標(biāo)
//如果超了仔沿,最后一個(gè)從mapModifiedTx選的,那么從中刪掉它尺棋,移到failedTx里
mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter);
}
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
nBlockMaxWeight - 4000) {
//最多重試次數(shù)到了封锉,區(qū)塊也快滿(mǎn)了绵跷,退出循環(huán)
break;
}
continue;
}
//找到iter的所有祖先
CTxMemPool::setEntries ancestors;
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
mempool.CalculateMemPoolAncestors(*iter, ancestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
//只需要未被確認(rèn)的祖先,已入塊的都去掉
onlyUnconfirmed(ancestors);
ancestors.insert(iter);
//確認(rèn)交易終結(jié)狀態(tài)成福,包括鎖定點(diǎn)和見(jiàn)證
if (!TestPackageTransactions(ancestors)) {
if (fUsingModified) {
mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter);
}
continue;
}
//交易可以入塊碾局,重試次數(shù)清零
nConsecutiveFailed = 0;
//交易及其所有祖先,先排好序
std::vector<CTxMemPool::txiter> sortedEntries;
SortForBlock(ancestors, sortedEntries);
//逐個(gè)入塊奴艾,并從mapModifiedTx中刪除
for (size_t i=0; i<sortedEntries.size(); ++i) {
AddToBlock(sortedEntries[i]);
mapModifiedTx.erase(sortedEntries[i]);
}
++nPackagesSelected;
//所有已入塊交易的子孫交易净当,更新相關(guān)信息
nDescendantsUpdated += UpdatePackagesForAdded(ancestors, mapModifiedTx);
}
}
OK,打包過(guò)程就是這樣蕴潦。下一節(jié)研究工作量證明(POW)像啼。
歡迎轉(zhuǎn)載,請(qǐng)注明出處潭苞。