Ripple中的Amendment与FeeVote
1. Amendment
Amendment是为了在XRP这种去中心化的网络中引用新特性而产生的,Amendments需要得到整个网络中80%的支持并且持续两周才能生效。Amendments一旦生效,后面的区块中会一直生效,要想禁用某一已经生效的Amendment,需要引入新的Amendment才能实现。
代码中设置两周生效
bool ApplicationImp::setup()
{
...
// Configure the amendments the server supports
{
Section supportedAmendments ("Supported Amendments");
supportedAmendments.append (detail::supportedAmendments ());
Section enabledAmendments = config_->section (SECTION_AMENDMENTS);
enabledAmendments.append (detail::preEnabledAmendments ());
//若要修改生效时间,可修改weeks{2},例如:std::chrono::minutes{10}
m_amendmentTable = make_AmendmentTable (
weeks{2},
MAJORITY_FRACTION,
supportedAmendments,
enabledAmendments,
config_->section (SECTION_VETO_AMENDMENTS),
logs_->journal("Amendments"));
}
...
}
使用方式
- 在验证节点中配置Amendment
1). 强制生效(一般用于开发测试,在单机模式下使用),这里只是配置当前节点启用特性
[features]
MultiSign
TrustSetAuth
2).投票设置(这个用处不大,即使不配置,节点程序会自动给它识别的特性投赞成票)
[amendments]
4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 MultiSign
42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE FeeEscalation
3).投反对票
[veto_amendments]
C1B8D934087225F509BEB5A8EC24447854713EE447D277F69545ABFA0E0FD490 Tickets
DA1BD556B42D85EA9C84066D028D355B52416734D3283F85E216EA5DA6DB7E13 SusPay
- 不做配置,在第一个标志区块(256的整数倍),会自动给识别的特性投赞成票,超过80%的赞成票且维持两周则在链中永久生效
Amendment的生效过程
每256个区块是一个标志(Flag)区块,在第一个Flag区块,进行投票
//第255个区块
template <class Traits>
void LedgerConsensusImp<Traits>::accept (TxSet_t const& set)
{
...
...
if (((sharedLCL->info().seq + 1) % 256) == 0)
// next ledger is flag ledger
{
// Suggest fee changes and new features
feeVote_.doValidation (sharedLCL, *v);
app_.getAmendmentTable ().doValidation (sharedLCL, *v);
}
}
第256个区块对投票结果进行统计,并添加虚拟交易:
//Callstack
timerEntry ->
statePreClose ->
closeLedger ->
takeInitialPosition ->
makeInitialPosition ->
LedgerConsensusImp<Traits>::makeInitialPosition () ->
std::pair <TxSet_t, Pos_t>
{
...
// Add pseudo-transactions to the set
if ((app_.config().standalone() || (proposing_ && haveCorrectLCL_))
&& ((previousLedger_->info().seq % 256) == 0))
{
// previous ledger was flag ledger, add pseudo-transactions
auto const validations =
app_.getValidations().getValidations (
previousLedger_->info().parentHash);
std::size_t const count = std::count_if (
validations.begin(), validations.end(),
[](auto const& v)
{
return v.second->isTrusted();
});
if (count >= app_.validators ().quorum ())
{
feeVote_.doVoting (
previousLedger_,
validations,
initialSet);
app_.getAmendmentTable ().doVoting (
previousLedger_,
validations,
initialSet);
}
}
}
doVoting函数中会调用一个重载的doVoting函数会为投票计数(注意第三个参数,后面会用到):
// Ask implementation what to do
auto actions = doVoting (
lastClosedLedger->parentCloseTime(),
getEnabledAmendments(*lastClosedLedger),
getMajorityAmendments(*lastClosedLedger),
parentValidations);
超过80%的Amendment会加入到一个变量名为actions的map中,value值为tfGotMajority
{
else if (hasValMajority &&
(majorityTime == NetClock::time_point{}) &&
! entry.second.vetoed)
{
// Ledger says no majority, validators say yes
JLOG (j_.debug()) <<
entry.first << ": amendment got majority";
actions[entry.first] = tfGotMajority;
}
}
actions返回后,在外层的doVoting函数中会在当前区块中注入 ttAMENDMENT 类型的交易:
// Inject appropriate pseudo-transactions
for (auto const& it : actions)
{
STTx amendTx (ttAMENDMENT,
[&it, seq = lastClosedLedger->seq() + 1](auto& obj)
{
obj.setAccountID (sfAccount, AccountID());
obj.setFieldH256 (sfAmendment, it.first);
obj.setFieldU32 (sfLedgerSequence, seq);
if (it.second != 0)
obj.setFieldU32 (sfFlags, it.second);
});
Serializer s;
amendTx.add (s);
initialPosition->addGiveItem (
std::make_shared <SHAMapItem> (
amendTx.getTransactionID(),
s.peekData()),
true,
false);
}
在下一区块中进行共识:
TER
Change::doApply()
{
if (ctx_.tx.getTxnType () == ttAMENDMENT)
return applyAmendment ();
assert(ctx_.tx.getTxnType() == ttFEE);
return applyFee ();
}
共识过程主要是将 gotMajority 的amendment加入到key为amendments的SLE中,并将上一区块的closetime设置到sfCloseTime字段中,两周后生效就是靠它来判断的
TER
Change::applyAmendment()
{
auto const k = keylet::amendments();
SLE::pointer amendmentObject =
view().peek (k);
if (gotMajority)
{
// This amendment now has a majority
newMajorities.push_back (STObject (sfMajority));
auto& entry = newMajorities.back ();
entry.emplace_back (STHash256 (sfAmendment, amendment));
// closetime set
entry.emplace_back (STUInt32 (sfCloseTime,
view().parentCloseTime().time_since_epoch().count()));
}
...
if (newMajorities.empty ())
amendmentObject->makeFieldAbsent (sfMajorities);
else
amendmentObject->setFieldArray (sfMajorities, newMajorities);
view().update (amendmentObject);
}
再后面,每256个区块同样会进行doVoting操作,但是内层doVoting中因为sfCloseTime字段的时间没满足两周,不会再添加到actions中,也就不会有pseudotransaction产生:
//获取到时间
{
auto const it = majorityAmendments.find (entry.first);
if (it != majorityAmendments.end ())
majorityTime = it->second;
}
直到两周后:
else if ((majorityTime != NetClock::time_point{}) &&
((majorityTime + majorityTime_) <= closeTime) &&
! entry.second.vetoed)
{
// Ledger says majority held
JLOG (j_.debug()) <<
entry.first << ": amendment majority held";
actions[entry.first] = 0;
}
满足两周的时间,actions的value值为设为0,这时注入交易中的flag不进行设置
// Inject appropriate pseudo-transactions
for (auto const& it : actions)
{
STTx amendTx (ttAMENDMENT,
[&it, seq = lastClosedLedger->seq() + 1](auto& obj)
{
obj.setAccountID (sfAccount, AccountID());
obj.setFieldH256 (sfAmendment, it.first);
obj.setFieldU32 (sfLedgerSequence, seq);
if (it.second != 0)
obj.setFieldU32 (sfFlags, it.second);
});
共识时,发现没有设置Flag字段,并且满足80%投票,则应用特性:
TER
Change::applyAmendment()
{
else if (!lostMajority)
{
// No flags, enable amendment
amendments.push_back (amendment);
amendmentObject->setFieldV256 (sfAmendments, amendments);
//重点
ctx_.app.getAmendmentTable ().enable (amendment);
if (!ctx_.app.getAmendmentTable ().isSupported (amendment))
{
JLOG (j_.error()) <<
"Unsupported amendment " << amendment <<
" activated: server blocked.";
ctx_.app.getOPs ().setAmendmentBlocked ();
}
}
}
2.FeeVoting
FeeVoting设置账户的预留费用及基础交易费用,这个配置不需要等两周,只需要在第一个256区块就会产生交易,第257个区块共识生效
可在配置文件中修改FeeVoting参数
[voting]
account_reserve = 3000000
owner_reserve = 1000000
reference_fee = 10