投票的分数是怎么计算的

score_of_vote=stake_of_EOS∗2^{current_year−2000}
举个例子,比如现在是2018年7月1号0点,我抵押了100个eos,那么我给超级节点A投了一票的分值就是:100 * 2^{18.5} = 37072760
过了半年,到了2019年1月1号0点,还是抵押了100个eos,那么我再次给超级节点A投了一票,那么节点A的总得分会先减去我前一票的分值37072760,然后再加上我当前的分值:100 * 2^{19}=52428800,详情可参见源码
当减少或者增加自己抵押的eos时,以前的投票的分数也会被即时更新,被投的节点总得分也会相应减少或增加。所以就是为什么你明明已经给某节点投过票,他们还在催你继续给他投的原因了,因为可能尽管你抵押的EOS数量没变,但你不同时刻的每一票的的分数是不一样的,它是随着时间递增的。

投票账户数是什么意思

投票账户数是从另一个维度来描述节点的支持率。表示有这么多个EOS用户来给他们喜欢的节点投票。不过,节点的排名并不是根据投票账户数来的,而是看上面讲的投票的总得分数来排序的。
所以你会经常发现有些节点的投票账户数很少,但是他们的得分却很高,原因就是有很多EOS大户给他们投票了,一个大户的一张票含金量非常高了。
前段时间发现一个很有意思的帐户 bpvoter.one,这个帐户下面创建了600多个小号,然后随便点进一个小号大概率都可以发现他们都抵押了2000到5000不等的EOS,这些小号基本上除了投票之外啥也不干,为啥不直接用一个帐号投呢。嘿嘿,人多热闹嘛,你懂的~

待申领奖励是怎么来的

节点们辛辛苦苦给我们出块、记账,活可不是白干的(每年的服务器成本还是很高的)。再加上EOS通过一些比如竞拍、买卖RAM过程中的手续费等操作会白白烧掉EOS,没有增发的话也会导致通货紧缩,所以有了每年增发不超过5%的EOS。那么这些增发的EOS是怎么分配的呢?
首先,增发的这部分EOS中的80%会打到一个叫 eosio.saving的账户里,可以看到现在为止已经有9百多万个EOS了,据说这账户里的EOS会用于社区建设。
然后,剩下的20%就是奖励给节点的了。这部分奖励又分两部分,其中的25%是作为出块奖励,剩下的75%就是我们这篇文章要介绍的投票奖励了。
那么这每年增发的5%里的20%里的75%的EOS是按什么规则分配给这么多节点的呢?

合约版本1.3.0之前的投票奖励分配规则

之前的分配规则比较简单粗暴,各自按照其得票的分数占比来瓜分这些奖励。以此刻的排名第一的eoshuobipool为例子,此时它的得分占比为2.596%,那么假如从现在开始它保持这个得票比率坚持一百年不动摇,那么它的得票奖励一年获得的奖励大致就是:
1000000000*0.05*0.2*0.75*0.02596 = 194700
每天的得票奖励大约为194700/365=533。不过这并不是一个节点的全部奖励,别忘了上面说过的还有出块奖励的哈。

合约版本1.3.0之后的投票奖励分配规则

终于来到本篇文章的重点了,该版本发布说明在 这里。新的分配规则在New producer pay algorithm章节里有说明,反正我是读不太懂,只能去源码里读全球通用语言了。
首先介绍一下大背景,新版本引入了一个叫做votepay share的概念,通过阅读代码我理解为这个新出的概念是一个类似于 得票分数随着时间成正比增长的股份 ,后面就是依据这个得票股份占比来分配投票奖励。这个得票股份的计算大致如下:
得票股份=得票分数*时间差
这个时间差就是任何两个 涉及到更新得票分数 的操作之间的时间差,比如:投票给某节点、增加/减少抵押的EOS、节点的申领奖励的操作等等。
如果仅仅是这样的话,其实跟旧规则差别不大,只是换个壳而已,但是其中有一个特别的限制:当节点有超过三天没有做claimrewards操作的时候,它就申领不到任何奖励,这些奖励会变相地被别的勤快的节点领走。
那么这个特别的限制会带来什么影响呢?你也许会说,现在的节点基本上都是天天领奖励,有些节点甚至都写了个脚本天天定时领,很少会有超过三天没领的。嗯,对于前21个出块节点来说是这样的,但是备选节点呢?我们知道通常备选节点没有机会出块,所以只有得票奖励,而得票奖励不满100EOS是无法领取的,所以就极有可能会造成三天没有claimrewards操作的尴尬情况。如果这次新版本的目的就是过滤掉垃圾备选节点的话,那我可以悄悄地告诉你一声,这个版本的系统合约是有bug的。
解释这个bug前,得先了解下新版本的逻辑,从两个action来阐述吧,一个是给节点投票的voteproducer,另一个就是节点的申领操作claimrewards。便于后面示例的解释,先给出一个宏定义:

公式的编号 公式的具体内容
A 节点得票股份+=节点得票分数*时间差
B 全网得票股份+=全网得票股份变化率*时间差
C 全网得票股份变化率+=节点得票分数
D 全网得票股份-=节点的得票股份
E 全网得票股份变化率-=节点得票分数
F 节点得票股份=0
G 节点奖励=(节点得分/全网总得分)*增发的得票EOS
H 节点奖励=(节点得票股份/全网得票股份)*增发的得票EOS
I 节点奖励=0

Voteproducer

示例编号 操作编号 节点的得票股份变化 全网的得票股份变化
0 vote0 A BC
1 vote1 F BDE
2 vote2 F B
3 vote3 A BC

Claimrewards
因为部署了新版本的系统合约后,还需要节点手动做一次updtrevision才开始走新版本的得票奖励结算逻辑,之后可以手动用regproducer重新更新一次节点,或者在下一次claimrewards的时候自动更新一次。
当updtrevision与regproducer/claimrewards之间无涉及到更新得票分数的操作时

示例编号 操作编号 节点获得的奖励 全网得票股份的变化
4 claim0 FG BC
5 claim1 ABHF D
6 claim2 I BADF

示例编号 操作编号 节点获得的奖励 全网得票股份的变化
7 claim1 I BDCF

当updtrevision与regproducer/claimrewards之间有涉及到更新得票分数的操作时

示例编号 操作编号 节点获得的奖励 全网得票股份的变化
8 vote0+claim0 ABHF D
9 vote0+claim0 AABHF D

示例总结

  • 通过示例1、2、6、7可以知道节点超过3天不claimrewards的话,得票奖励会被清0,损失的奖励继续在得票奖励池子里。等于变相的增加了其他节点的奖励。
  • 通过示例8、9可以知道节点在做完updtrevision操作后,要尽快执行一次regproducer或者claimrewards操作,否则会丢失这期间的可能的来自用户投票的奖励。这也是原文里最后一句话说可能遗失某些奖励的原因。

关于bug

现在再来解释前面提到的bug,这个bug存在于判断本次claimrewards离上次claimrewards是否已经超过3天。
相关源码如下:

if( producer_per_vote_pay < min_pervote_daily_pay ) {
         producer_per_vote_pay = 0;
}

......

_producers.modify( prod, 0, [&](auto& p) {
   p.last_claim_time = ct;
   p.unpaid_blocks   = 0;
});

在判断完得票奖励不足100EOS时,没有做任何处理,代码会径直走到p.last_claim_time = ct;这一行,个人觉得这应该是个bug,因为备选几点可以写个定时脚本天天做一次claimrewards操作,尽管他们可能并不能申领到任何奖励,但是依旧能成功刷新他们的last_claim_time时间。这样的话,新版本的关于超过3天未做claimrewards的判断就不可能成功了。

原文地址:https://articles.eospark.com/?p=74