Aleo收益分配機制GitHub代碼解讀

Engilsh

官方在其最新活躍的代碼開發分支中更新了獎勵機制部分
雖然這一部分不一定是主網上運行的最終確定版
但是我們可以透過目前開發的代碼,了解一下官方對獎勵機制可能的設計.

獎勵機制的代碼在: https://github.com/AleoHQ/snarkOS/blob/feat%2Fexperimental-networking2/consensus/src/rewards/mod.rs

在開始解讀代碼之前, 我們先回答幾個最重要,大家最關心的問題:

Q:礦工還有收益嗎?
A:有,主網上線 10 年內都有挖礦收益, 10年中每年遞減, 直到10年後,出塊獎勵歸零.

Q:礦工一個塊能獲得多少收益
A:第一年在 80 個 aleo 左右, 後續十年,每年遞減

Q:validator 門檻是多少? 能獲得多少收益?
A:100萬 aleo, 每個塊分給 validator 的是 11 個 Aleo (星號)

接下來, 我們帶大家一起詳細的看一下代碼:

先看一下代碼中定義的一些數值:

1
const NUM_GATES_PER_CREDIT: u64 = 1_000_000; // 1 million gates == 1 credit

定義兩種代幣 gates/credit 之間的換算關係: 1 credit = 100萬 gates

1
const STARTING_SUPPLY: u64 = 1_000_000_000 * NUM_GATES_PER_CREDIT; // 1 quadrillion gates == 1 billion credits

初始供應: 10億枚 aleo

1
const STAKING_PERCENTAGE: f64 = 0.025f64; // 2.5%

質押比例, 跟 validator 的收益有關, 後面我們會解讀

1
const ANCHOR_TIMESTAMP: u64 = 1640179531; // 2019-01-01 00:00:00 UTC

錨定時間, 應該是主網的啟動時間, 目前先設定為 2019-01-01 00:00:00 UTC

1
const ANCHOR_TIME: u64 = 15; // 15 seconds

出塊時間, 是的, aleo 的出塊時間很有可能從 testnet2 的 20s 調整成 15s

接著我們就可以看代碼中的最重要的兩個函數:

設定質押獎勵的: staking_reward 函數

1
2
3
4
5
6
7
8
9
10
11
/// 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,

注:

  1. 注意我們每個公式中的 aleo 都要乘以 100萬倍換算成 gates, 然後再帶入計算, 得出一個數值
    下面也是相同的, 不再做此提示.

  2. 這裡不是說成為 validator 就能每個塊都能獲取這麼多獎勵, 只是說 validator 們能夠從中分到 11 個 aleo, 具體網絡上會有多少 validator, 以及每個塊
    中的獎勵如何在 validator 中去分, 歡迎關注我們, 我們會在官網代碼進一步完備後做第一時間的解讀.

再來看礦工收益: coinbase_reward 函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
7
/// 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, 我們再回頭看出塊獎勵的代碼:

1
2
3
4
5
6
7
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);

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 的解讀

  1. 塊差越大, factor 就越大,礦工出塊收益越小
  2. validator 個數越大, factor 就越小, 礦工出塊收益就越大

所以 aleo 團隊通過這個 factor 的設計來引導礦工

  1. 將出塊頻率控制在 15s 作妖, 不要產生較大的塊差
  2. 積極參與質押,成為 validator

帶入各個參數後, 我們算得

第一年主網啟動時, 礦工收益: 83 Aleo
第二年: 67 Aleo
第三年: 58 Aleo

第十年: 8 Aleo

第十一年: 0

注:本文所述的獎勵分配機制為官方 GitHub 代碼解讀而來,並非最終確定版,更多消息請關注 Aleo官方公告!

鏈接已復制!