ripple (瑞波)區塊鏈網絡手續費fee參數 : load_factor參數算法詳解

load_factor參數算法詳解

server_info的load_factor

//src\ripple\app\misc\NetworkOPs.cpp : getServerInfo
info[jss::load_factor] = static_cast<double> (loadFactor) / loadBaseServer;

參數說明:

  1. loadBaseServer爲load_factor的基礎初始值:lftNormalFee = 256;
  2. loadFactor由以下參數決定:
auto const escalationMetrics = app_.getTxQ().getMetrics(*app_.openLedger().current());
auto const loadFactorServer = app_.getFeeTrack().getLoadFactor();
auto const loadBaseServer = app_.getFeeTrack().getLoadBase();
auto const loadFactorFeeEscalation = escalationMetrics ? escalationMetrics->expFeeLevel : 1;
auto const loadBaseFeeEscalation = escalationMetrics ? escalationMetrics->referenceFeeLevel : 1;

auto const loadFactor = std::max(static_cast<std::uint64_t>(loadFactorServer),
        mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second);

2.1. mulDiv函數定義如下:
給定三個參數A,B,C,結果result=A*B/C; 當result超過uint64_t的最大值limit64時,返回limit64,否則返回result。
2.2. loadFactorServer實際爲{ clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_ }三個值取最大值。
這部分涉及本地及遠程負載費用計算,在loadFactorServer部分詳細介紹;
2.3. loadFactorFeeEscalation和loadBaseFeeEscalation都是由getMetrics返回,這涉及到ripple的FeeEscalation特性

loadFactorServer

loadFactorServer爲{ clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_ }三個值取最大值,cluster模式
目前沒有使用,暫時不討論。主要介紹localTxnLoadFee_和remoteTxnLoadFee_ ;

1. localTxnLoadFee_

主要由LoadManger類來負責管理:

//src\ripple\app\main\LoadManager.cpp : run ()
bool change = false;
if (app_.getJobQueue ().isOverloaded ()) {
    JLOG(journal_.info()) << app_.getJobQueue ().getJson (0);
    change = app_.getFeeTrack ().raiseLocalFee ();
}
else {
    change = app_.getFeeTrack ().lowerLocalFee ();
}

if (change) {
    app_.getOPs ().reportFeeChange ();
}

從上述代碼中,確定此處有四部分因素:

  • job是否過載;
  • raiseLocalFee;
  • lowerLocalFee;
  • reportFeeChange;

整體過程概述爲:
首先判斷當前job是否過載,即isOverloaded函數。該函數遍歷當前每一個執行的job,如果有任意一個job被判斷爲過載,
則返回true,觸發raiseLocalFee函數。否則觸發lowerLocalFee。只有連續兩次觸發raiseLocalFee,纔會調整fee。無論fee
是增大或者減小,只要有變化就會調用reportFeeChange向peer廣播本地fee。其他節點收到廣播後將其設置爲remoteTxnLoadFee_。

1.1. isOverloaded()
該函數遍歷每個當前job。進行過載判斷,只要有一個過載,就返回ture,觸發raiseLocalFee。
每個job都會有一個過載樣本庫,每次當jobEvent停止時,運行時間加上等待時間被記爲延遲時間,超過2ms,就會被加入過載樣本庫。
樣本庫計數mLatencyEvents加一,延遲均值和延遲峯值都會增加當前樣本延遲量。
當mLatencyEvents值不等於零,就將延遲均值和延遲峯值和該job設定的預期延遲均值mTargetLatencyAvg和預期延遲峯值mTargetLatencyPk進行比較:(預期延遲值在job的定義中給出)

isOverTarget (mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4));
bool LoadMonitor::isOverTarget (std::uint64_t avg, std::uint64_t peak)
{
    return (mTargetLatencyPk && (peak > mTargetLatencyPk)) ||
           (mTargetLatencyAvg && (avg > mTargetLatencyAvg));
}

根據比較結果判斷是否過載。

1.2. raiseLocalFee()
該函數通過一個計數器控制,每次調用該函數,計數器加一,只有該計數器大於2時,纔會調整,否則直接返回,而每次調用lowerLocalFee都會將該計數器減一,最少爲0。所以需要連續兩次觸發reaiseLocalFee,纔會有效。
函數內部首先將localTxnLoadFee_與remoteTxnLoadFee_比較,取最大值,然後增加該值的1/4,並與上限lftFeeMax比較,超過上限返回上限,否則返回計算結果。

1.3. lowerLocalFee()
每次調用首先將計數器減一,然後減少localTxnLoadFee_的1/4,並與基礎值lftNormalFee比較,小於基礎值,返回基礎值,否則返回計算結果。

1.4. reportFeeChange()
無論費用是增加或者減少,只有有變動,就會通過該函數,廣播給peer節點。其他節點收到後設置爲該節點的remoteTxnLoadFee_

2. remoteTxnLoadFee_

即其他信任節點廣播的localTxnLoadFee_的值。

FeeEscalation特性

getMetrics函數

//src\ripple\app\misc\impl\TxQ.cpp : getMetrics

在getMetrics函數中,有三個決定因素,分別是當前交易量,期望交易量和倍乘器。

1. 期望交易量算法:

鏈啓動後,對於每個區塊設定一個交易數量最小值,期望值和最大值。除了程序預置的以下值,
還可以在配置文件中的[transaction_queue]下手動配置。

StandAlone Normal
最小值 1000 5
期望值 1000 50
最大值 NULL NULL

期望交易量就是
上一區塊的實際交易量與預置最小值和預置期望值比較,小於最小取最小,大於期望取期望,否則取原本值。
(這是一種情況的取值,另一種情況算法詳見chainsqld\src\ripple\app\misc\impl\TxQ.cpp : TxQ::FeeMetrics::update)。

2. 倍乘器算法:

鏈啓動後,設置一個初始最小倍乘器:minimumEscalationMultiplier = 256 * 500。
倍乘器實際算法:(feeLevels是一個vector,按順序存儲當前區塊所有交易的手續費)

if (feeLevels.empty())
    {
        escalationMultiplier_ = minimumMultiplier_;
    }
    else
    {
        escalationMultiplier_ = (feeLevels[size / 2] +
            feeLevels[(size - 1) / 2] + 1) / 2;
        escalationMultiplier_ = std::max(escalationMultiplier_,
            minimumMultiplier_);
    }

3. 計算最後expFeeLevel

FeeEscalation特性就是根據當前openledger的交易量和由上一區塊的交易量計算出的期望交易量進行比較:

if (current > target)
{
    return mulDiv(multiplier, current * current, target * target).second;
}

return baseLevel;

最後計算

至此,將server_info中計算load_factor的所有參數準備完畢,並依據文章開頭的公式:

auto const loadFactor = std::max(static_cast<std::uint64_t>(loadFactorServer),
        mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second);

計算出load_factor。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章