官方在其最新活跃的代码开发分支中更新了奖励机制部分虽然这一部分不一定是主网上运行的最终确定版但是我们可以透过目前开发的代码,了解一下官方对奖励机制可能的设计.
奖励机制的代码在: https://github.com/AleoHQ/snarkOS/blob/feat/experimental-networking2/consensus/src/rewards/mod.rs
在开始解读代码之前, 我们先回答几个最重要,大家最关心的问题:
问:prover还有收益吗?
答: 有,主网上线 10 年内都有prover收益, 10年中每年递减, 直到10年后,出块奖励归零.
问:prover一个块能获得多少收益?
答: 第一年在 80 个 aleo 左右, 后续十年,每年递减
问:validator 门槛是多少? 能获得多少收益?
答: 100万 aleo, 每个块分给 validator 的是 11 个 Aleo (*后续解答)
接下来, 我们带大家一起详细地看一下代码:
先看一下代码中定义的一些数值:
const NUM_GATES_PER_CREDIT: u64 = 1_000_000; // 1 million gates == 1 credit
定义两种代币 gates/credit 之间的换算关系: 1 credit = 100万 gates
const STARTING_SUPPLY: u64 = 1_000_000_000 * NUM_GATES_PER_CREDIT; // 1 quadrillion gates == 1 billion credits
初始供应: 10亿枚 aleo
const STAKING_PERCENTAGE: f64 = 0.025f64; // 2.5%
质押比例, 跟 validator 的收益有关, 后面我们会解读
const ANCHOR_TIMESTAMP: u64 = 1640179531; // 2019-01-01 00:00:00 UTC
锚定时间, 应该是主网的启动时间, 目前先设定为 2019-01-01 00:00:00 UTC
const ANCHOR_TIME: u64 = 15; // 15 seconds
出块时间, 是的, aleo 的出块时间很有可能从 testnet2 的 20s 调整成 15s
接着我们就可以看代码中的最重要的两个函数:
设定质押奖励的: staking_reward 函数 https://github.com/AleoHQ/snarkOS/blob/d8b852977b0dd1800488ba45daf74f6f09729ec4/consensus/src/rewards/mod.rs#L30-L39
/// Calculate the staking reward, given the starting supply and anchor time. pub fn staking_reward<const STARTING_SUPPLY: u64, const ANCHOR_TIME: u64>() -> u64 { // The staking percentage at genesis. const STAKING_PERCENTAGE: f64 = 0.025f64; // 2.5% let block_height_around_year_1 = estimated_block_height(ANCHOR_TIME, 1); let reward = (STARTING_SUPPLY as f64 * STAKING_PERCENTAGE) / block_height_around_year_1 as f64; return reward.floor() as u64; }
可以看到, 该函数没有普通的参数,只有两个 const 常量参数,所以一旦主网上线, 确定了初始供应和出块间隔后, 每个块中的质押奖励的值就应该是不变的.
奖励的计算方法为:
奖励 = (初始供应 * 质押占比) / 一年总出块数
其中一年总出块数的计算是确定出块间隔后, 用一年的总秒数除以出块间隔 (15s)
我们带入目前已经有的一些参数
奖励 = 10亿 aleo 初始供应 * 占比 2.5% / 一年出块数: (365 * 24 * 3600 / 15) ~= 11.89
最后向下取整为 11
即每个块, validator(们) 可以获得 11 个 aleo,
注:
注意我们每个公式中的 aleo 都要乘以 100万倍换算成 gates, 然后再带入计算, 得出一个数值 下面也是相同的, 不再做此提示.
这里不是说成为 validator 就能每个块都能获取这么多奖励, 只是说 validator 们能够从中分到 11 个 aleo, 具体网络上会有多少 validator, 以及每个块 中的奖励如何在 validator 中去分, 欢迎关注我们, 我们会在官网代码进一步完备后做第一时间的解读.
再来看矿工收益: coinbase_reward 函数 https://github.com/AleoHQ/snarkOS/blob/d8b852977b0dd1800488ba45daf74f6f09729ec4/consensus/src/rewards/mod.rs#L42-L56
pub fn coinbase_reward<const STARTING_SUPPLY: u64, const ANCHOR_TIMESTAMP: u64, const ANCHOR_TIME: u64>( num_validators: u64, timestamp: u64, block_height: u64, ) -> f64 { let block_height_around_year_10 = estimated_block_height(ANCHOR_TIME, 10); let max = std::cmp::max(block_height_around_year_10.saturating_sub(block_height), 0); let anchor_reward = anchor_reward::<STARTING_SUPPLY, ANCHOR_TIME>(); let factor = factor::<ANCHOR_TIMESTAMP, ANCHOR_TIME>(num_validators, timestamp, block_height); let reward = (max * anchor_reward) as f64 * 2f64.powf(-1f64 * factor); reward }
矿工奖励的计算比较复杂, 通过函数定义,我们可以看到, 出块奖励是动态变化的, 跟 num_validators: validator 的个数, timestamp: 出块时的时间戳, block_height 块高这三个 动态参数有关
我们一步一步来看 首先我们需要了解 anchor_reward: https://github.com/AleoHQ/snarkOS/blob/d8b852977b0dd1800488ba45daf74f6f09729ec4/consensus/src/rewards/mod.rs#L20-L27
pub fn anchor_reward<const STARTING_SUPPLY: u64, const ANCHOR_TIME: u64>() -> u64 { let block_height_around_year_10 = estimated_block_height(ANCHOR_TIME, 10); let numerator = 2 * STARTING_SUPPLY; let denominator = block_height_around_year_10 * (block_height_around_year_10 + 1); (numerator as f64 / denominator as f64).floor() as u64 }
anchor_reward 是一个锚定的基准值
这个值也是确定了初始供应和出块间隔后就不再变化了
简单来说 anchor_reward = 2 * 初始供应 / (十年出块总数的平方)
我们带入计算一下 anchor_reward = 2 * 初始供应 10 aleo 亿 / (十年出块: 21024000 的平方) ~= 4.524 最后向下取整为: 4
然后矿工奖励计算中还涉及一个用来动态调节的函数 factor: https://github.com/AleoHQ/snarkOS/blob/d8b852977b0dd1800488ba45daf74f6f09729ec4/consensus/src/rewards/mod.rs#L58-L64
/// Calculate the factor used in the target adjustment algorithm and coinbase reward. fn factor<const ANCHOR_TIMESTAMP: u64, const ANCHOR_TIME: u64>(num_validators: u64, timestamp: u64, block_height: u64) -> f64 { let numerator: f64 = (timestamp as f64 - ANCHOR_TIMESTAMP as f64) - (block_height as f64 * ANCHOR_TIME as f64); let denominator = num_validators * ANCHOR_TIME; numerator as f64 / denominator as f64 }
factor = 分子 / 分母
分子: (当前时间 - 初始时间) - (当前块高 * 出块间隔) 可以看到, 分子表示的是理论时间出块时间的时间差 比如: aleo 主网的启动时间设置为 2023-01-01 00:00:00 UTC, 理论上来说, 15s 出一个块, 那么出了 24 * 3600 / 15 = 5760 个块之后, 当前时间应该是 2023-01-02 00:00:00 UTC, 但实际上当前时间可能会有一定的偏差, 比如 2023-01-02 00:00:05 UTC, 慢了 5s. 这里分子算的就是这个偏差
分母: validator 个数 * 出块间隔
我们化简下,直接先用分子除以出块间隔得到以块为单位的时间差
所以, 这个 factor 实际上是: 块差 / validator 个数
factor 最终会被引入到出块奖励中, 用来引导矿工调整出块频率, 同时也用来引导参与 validator 的个数 这里我们可以看到, 块差越大, factor 就越大, validator 个数越大, factor 就越小
有了 anchor_reward 和 factor, 我们再回头看出块奖励的代码:
let block_height_around_year_10 = estimated_block_height(ANCHOR_TIME, 10);
let max = std::cmp::max(block_height_around_year_10.saturating_sub(block_height), 0); let anchor_reward = anchor_reward::
reward = (block_diff * anchor_reward) * 2.powf(-1f64 * factor);
block_diff 是 (十年出块总数 - 当前块高), 当当前块高超过十年出块总数时, 这个值取 0 通过这个 block_diff 我们就可以看出, 其它条件不变, 块高越高, 收入越少, 知道出 10 年的块后, 奖励归零
anchor_reward 我们之前已经算过了, 初始供应 10亿 aleo, 出块间隔为 4 的情况下, 这个值为 4
矿工如果想获得更多的收益, 前面的这些部分都是没法调节的 最后这一部分 1 / 2^factor
回到前面对 factor 的解读
块差越大, factor 就越大,矿工出块收益越小
validator 个数越大, factor 就越小, 矿工出块收益就越大
所以 aleo 团队通过这个 factor 的设计来引导矿工
将出块频率控制在 15s 作妖, 不要产生较大的块差
积极参与质押,成为 validator
带入各个参数后, 我们算得
第一年主网启动时, 矿工收益: 83 第二年: 67 第三年: 58 ... 第十年: 8
第十一年: 0
注:本文所述的奖励分配机制为ZKRush团队从官方GitHub 代码解读而来,并非最终确定版,更多消息请关注 Aleo官方公告并欢迎大家加入ZKRush官方Discord 参与讨论!
关于 ZKRush: ZKRush致力於打造最穩定的零知識證明礦池,我們提供精準的算力數據,領先全網的挖礦效率以及高效準確的收益分配模式,並能夠在GPU算法上做到全網領先。 ZKRush的高兼容性挖礦程序適配所有主流設備,服務全球礦工。
Discord: https://discord.gg/bjTBEMzQKG
Twitter: https://twitter.com/zkrush_official
发表评论 取消回复