區塊鏈共識演算法(3)PoS權益證明演算法
# PoS權益證明演算法原理及其在點點幣、黑幣中的實現
PoS,即Proof of Stake,譯為權益證明。
無論PoW或PoS,均可以理解為“誰有資格寫區塊鏈”的問題。
PoW通過算力證明自己有資格寫區塊鏈,而PoS則是通過擁有的幣齡來證明自己有資格寫區塊鏈。
### PoW的優勢和弊端
PoW,優勢為可靠,使用廣泛,是經歷了充分的實踐檢驗的公有鏈共識演算法。
但其缺點也較為明顯:
* 1、消耗了太多額外算力,即大量能源。
* 2、資本大量投資礦機,導致算力中心化,有51%攻擊的安全隱患。
### PoS的提出和點點幣
第一個基於PoS的虛擬幣是點點幣。
鑑於PoW的缺陷,2012年Sunny King提出了PoS,並基於PoW和PoS的混合機制釋出了點點幣PPCoin。
前期採用PoW挖礦開採和分配貨幣,以保證公平。後期採用PoS機制,保障網路安全,即擁有51%貨幣難度更大,從而防止51%攻擊。
PoS核心概念為幣齡,即持有貨幣的時間。例如有10個幣、持有90天,即擁有900幣天的幣齡。
另外使用幣,即意味著幣齡的銷燬。
在PoS中有一種特殊的交易稱為利息幣,即持有人可以消耗幣齡獲得利息,同時獲得為網路產生區塊、以及PoS造幣的優先權。
### 點點幣的PoS實現原理
點點幣的PoS證明計算公式為:
proofhash < 幣齡x目標值
展開如下:
hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget x bnCoinDayWeight
* 其中proofhash,對應一組資料的雜湊值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)。
* 幣齡即bnCoinDayWeight,即幣天,即持有的幣數乘以持有幣的天數,此處天數最大值為90天。
* 目標值,即bnTarget,用於衡量PoS挖礦難度。目標值與難度成反比,目標值越大、難度越小;反之亦然。
由公式可見,持有的幣天越大,挖到區塊的機會越大。
peercoin-0.6.1ppc中PoS證明計算程式碼如下:
```c++
bool CheckStakeKernelHash(unsigned int nBits, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake)
{
if (nTimeTx < txPrev.nTime) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
return error("CheckStakeKernelHash() : min age violation");
//目標值使用nBits
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
int64 nValueIn = txPrev.vout[prevout.n].nValue;
// v0.3 protocol kernel hash weight starts from 0 at the 30-day min age
// this change increases active coins participating the hash and helps
// to secure the network when proof-of-stake difficulty is low
int64 nTimeWeight = min((int64)nTimeTx - txPrev.nTime, (int64)STAKE_MAX_AGE) - (IsProtocolV03(nTimeTx)? nStakeMinAge : 0);
//計算幣齡,STAKE_MAX_AGE為90天
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
// Calculate hash
CDataStream ss(SER_GETHASH, 0);
//權重修正因子
uint64 nStakeModifier = 0;
int nStakeModifierHeight = 0;
int64 nStakeModifierTime = 0;
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
{
if (!GetKernelStakeModifier(blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
return false;
ss << nStakeModifier;
}
else // v0.2 protocol
{
ss << nBits;
}
//計算proofhash
//即計算hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)
ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
hashProofOfStake = Hash(ss.begin(), ss.end());
if (fPrintProofOfStake)
{
if (IsProtocolV03(nTimeTx))
printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime).c_str(),
mapBlockIndex[blockFrom.GetHash()]->nHeight,
DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
printf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
IsProtocolV05(nTimeTx)? "0.5" : (IsProtocolV03(nTimeTx)? "0.3" : "0.2"),
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64) nBits,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString().c_str());
}
// Now check if proof-of-stake hash meets target protocol
//判斷是否滿足proofhash < 幣齡x目標值
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
return false;
if (fDebug && !fPrintProofOfStake)
{
if (IsProtocolV03(nTimeTx))
printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime).c_str(),
mapBlockIndex[blockFrom.GetHash()]->nHeight,
DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
printf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
IsProtocolV03(nTimeTx)? "0.3" : "0.2",
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64) nBits,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString().c_str());
}
return true;
}
//程式碼位置src/kernel.cpp
```
### 點點幣的PoS挖礦難度
點點幣使用目標值來衡量挖礦難度,目標值與難度成反比,目標值越大、難度越小;反之亦然。
當前區塊的目標值與前一個區塊目標值、前兩個區塊的時間間隔有關。
計算公式如下:
當前區塊目標值 = 前一個區塊目標值 x (1007x10x60 + 2x前兩個區塊時間間隔) / (1009x10x60)
由公式可見,兩個區塊目標間隔時間即為10分鐘。
如果前兩個區塊時間間隔大於10分鐘,目標值會提高,即當前區塊難度會降低。
反之,如果前兩個區塊時間間隔小於10分鐘,目標值會降低,即當前區塊難度會提高。
peercoin-0.6.1ppc中目標值計算程式碼如下:
```c++
unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
if (pindexLast == NULL)
return bnProofOfWorkLimit.GetCompact(); // genesis block
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
if (pindexPrev->pprev == NULL)
return bnInitialHashTarget.GetCompact(); // first block
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
if (pindexPrevPrev->pprev == NULL)
return bnInitialHashTarget.GetCompact(); // second block
int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
// ppcoin: target change every block
// ppcoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
bnNew.SetCompact(pindexPrev->nBits);
//STAKE_TARGET_SPACING為10分鐘,即10 * 60
//兩個區塊目標間隔時間即為10分鐘
int64 nTargetSpacing = fProofOfStake? STAKE_TARGET_SPACING : min(nTargetSpacingWorkMax, (int64) STAKE_TARGET_SPACING * (1 + pindexLast->nHeight - pindexPrev->nHeight));
//nTargetTimespan為1周,即7 * 24 * 60 * 60
//nInterval為1008,即區塊間隔為10分鐘時,1周產生1008個區塊
int64 nInterval = nTargetTimespan / nTargetSpacing;
//計算當前區塊目標值
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
bnNew /= ((nInterval + 1) * nTargetSpacing);
if (bnNew > bnProofOfWorkLimit)
bnNew = bnProofOfWorkLimit;
return bnNew.GetCompact();
}
//程式碼位置src/kernel.cpp
```
### PoS 2.0的提出和黑幣
為了進一步鞏固PoS的安全,2014年rat4(Pavel Vasin)提出了PoS 2.0,併發布了黑幣。
黑幣前5000個塊,為純PoW階段;第5001個塊到第10000個塊為PoW與PoS並存階段,從第10001個塊及以後為純PoS階段。
黑幣首創快速挖礦+低股息發行模式,發行階段採用POW方式,通過演算法改進在短時間內無法制造出專用的GPU和AISC礦機,解決分配不公平的問題。
PoS2.0相比PoS的改進:
* 1、將幣齡從等式中拿掉。新系統採用如下公式計算權益證明:
proofhash < 幣數x目標值
點點幣中,部分節點平時保持離線,只在積累了可觀的幣齡以後才連線獲取利息,然後再次離線。
PoS 2.0中拿掉幣齡,使得積攢幣齡的方法不再有效,所有節點必須更多的保持線上,以進行權益累積。
越多的節點線上進行權益累積,系統遭遇51%攻擊的可能性就越低。
* 2、為了防範預先計算攻擊,權益修正因子每次均改變。
* 3、改變時間戳規則,以及雜湊演算法改用SHA256。
### 黑幣的PoS實現原理
黑幣的PoS證明計算公式為:
proofhash < 幣數x目標值
展開如下:
hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight
其中proofhash,對應一組資料的雜湊值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)。
幣數即nWeight,目標值即bnTarget。
blackcoin-1.2.4中PoS證明計算程式碼如下:
```c++
static bool CheckStakeKernelHashV2(CBlockIndex* pindexPrev, unsigned int nBits, unsigned int nTimeBlockFrom, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
{
if (nTimeTx < txPrev.nTime) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");
//目標值使用nBits
CBigNum bnTarget;
bnTarget.SetCompact(nBits);
//計算幣數x目標值
int64_t nValueIn = txPrev.vout[prevout.n].nValue;
CBigNum bnWeight = CBigNum(nValueIn);
bnTarget *= bnWeight;
targetProofOfStake = bnTarget.getuint256();
//權重修正因子
uint64_t nStakeModifier = pindexPrev->nStakeModifier;
uint256 bnStakeModifierV2 = pindexPrev->bnStakeModifierV2;
int nStakeModifierHeight = pindexPrev->nHeight;
int64_t nStakeModifierTime = pindexPrev->nTime;
//計算雜湊值
//即計算hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)
CDataStream ss(SER_GETHASH, 0);
if (IsProtocolV3(nTimeTx))
ss << bnStakeModifierV2;
else
ss << nStakeModifier << nTimeBlockFrom;
ss << txPrev.nTime << prevout.hash << prevout.n << nTimeTx;
hashProofOfStake = Hash(ss.begin(), ss.end());
if (fPrintProofOfStake)
{
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime),
DateTimeStrFormat(nTimeBlockFrom));
LogPrintf("CheckStakeKernelHash() : check modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier,
nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString());
}
// Now check if proof-of-stake hash meets target protocol
//判斷是否滿足proofhash < 幣數x目標值
if (CBigNum(hashProofOfStake) > bnTarget)
return false;
if (fDebug && !fPrintProofOfStake)
{
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime),
DateTimeStrFormat(nTimeBlockFrom));
LogPrintf("CheckStakeKernelHash() : pass modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier,
nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString());
}
return true;
}
```
### 附錄
* [點點幣github](https://github.com/peercoin)
* [黑幣github](https://github.com/CoinBlack/blackcoin)
* [點點幣白皮書(中文版)](https://github.com/fengchunjian/ConsensusAlgorithm/blob/master/pos/%E7%82%B9%E7%82%B9%E5%B8%81%E7%99%BD%E7%9A%AE%E4%B9%A6%EF%BC%88%E4%B8%AD%E6%96%87%E7%89%88%EF%BC%89.pdf)
* [黑幣PoS協議2.0版白皮書](https://github.com/fengchunjian/ConsensusAlgorithm/blob/master/pos/%E9%BB%91%E5%B8%81PoS%E5%8D%8F%E8%AE%AE2.0%E7%89%88%E7%99%BD%E7%9A%AE%E4%B9%A6.pdf)
### 後記
PoS有種種優點,但也有所缺陷。
即因為PoS並不消耗更多的算力,因此如果出現分叉,理性節點會在所有鏈上同時PoS挖礦。
以至於每次分叉都會形成新的山寨幣,即PoS無法很好的應對分叉。
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- 區塊鏈共識演算法(5)DPoS股份授權證明演算法區塊鏈演算法
- (二)區塊鏈的共識演算法:PoS 及其 例子 程式碼 實現區塊鏈演算法
- 區塊鏈主流共識演算法區塊鏈演算法
- 可用於區塊鏈的共識演算法區塊鏈演算法
- 區塊鏈主流共識演算法彙總區塊鏈演算法
- 一個基於PoS共識演算法的區塊鏈例項解析(升級版)演算法區塊鏈
- 第4章 區塊鏈靈魂:共識演算法區塊鏈演算法
- 區塊鏈中五種常見共識演算法區塊鏈演算法
- 讀懂區塊鏈共識機制 :PoW、PoS、PAXOS、RAFT、PBFT區塊鏈Raft
- 區塊鏈共識機制技術一--POW(工作量證明)共識機制區塊鏈
- 解密區塊鏈最強心臟 迅雷鏈共識演算法詳解解密區塊鏈演算法
- 區塊鏈共識之Paxos演算法理解與實戰區塊鏈演算法
- 【阿菜讀論文】區塊鏈共識演算法綜述區塊鏈演算法
- 萬字長文:解讀區塊鏈7類共識演算法區塊鏈演算法
- 區塊鏈共識演算法(1)分散式一致性演算法Raft區塊鏈演算法分散式Raft
- 區塊鏈共識演算法(4)分散式一致性演算法Paxos區塊鏈演算法分散式
- 區塊鏈新演算法:人類證明(Proof of Human:PoH) - santisiri區塊鏈演算法
- 區塊鏈共識機制區塊鏈
- 共識演算法PoS及Go語言實現演算法Go
- POS權益證明機制的去中心化是偽命題中心化
- 區塊鏈知識系列 - PBFT 共識區塊鏈
- 區塊鏈知識系列 - Raft 共識區塊鏈Raft
- 共識問題:區塊鏈如何確認記賬權?區塊鏈
- 區塊鏈共識演算法(6)分散式一致性演算法2PC和3PC區塊鏈演算法分散式
- Python實現一條基於POS演算法的區塊鏈Python演算法區塊鏈
- 共識演算法之爭(PBFT,Raft,PoW,PoS,DPoS,Ripple)演算法Raft
- 區塊鏈共識的確定性區塊鏈
- 區塊鏈100講: 區塊鏈共識的確定性區塊鏈
- 一文帶你瞭解區塊鏈中15種共識演算法區塊鏈演算法
- 區塊鏈演算法區塊鏈演算法
- 被證明的黎曼猜想跟區塊鏈加密演算法有什麼關係?區塊鏈加密演算法
- (一)區塊鏈的共識演算法:整體介紹 及 分叉 的通俗講解區塊鏈演算法
- 區塊鏈共識機制的演進區塊鏈
- 第3章 區塊鏈骨骼:密碼演算法區塊鏈密碼演算法
- 區塊鏈共識演算法(2)PoW挖礦演算法原理及其在比特幣、以太坊中的實現區塊鏈演算法比特幣
- 近幾天對區塊鏈中幾種常見的共識機制(PBFT,Raft,PoW,PoS,DPoS,Ripple)區塊鏈Raft
- 區塊鏈概念1:Hash演算法區塊鏈演算法
- 016 | 漫談區塊鏈共識機制區塊鏈