作者:黎躍春 孔壹學院創始人兼CEO 黎躍春:孔壹學院創始人兼CEO,國內區塊鏈佈道先行者,通信和信息技術培養工程區塊鏈高級授課專家。 從零到壹學習共識算法為一個系列,一共18講,包括通過一個App的演示深入理解區塊鏈運行原理、go 實現簡單的區塊鏈、拜占庭、PoW、PoS、DPoS、Paxos、Raft等,為大家詳盡的介紹共識算法的學習過程。今天我們將為大家介紹從零到壹學習共識算法第九講:共識機制-權益證明 PoS。話不多說,馬上開啟我們的共識算法學習之旅。 點點幣PPCoin 前期採用PoW挖礦開採和分配貨幣,以保證公平。後期採用PoS機制,保障網絡安全,即擁有51%貨幣難度更大,從而防止51%攻擊。 PoS核心概念為幣齡,即持有貨幣的時間。例如有10個幣、持有90天,即擁有900幣天的幣齡。另外使用幣,即意味著幣齡的銷燬。 在PoS中有一種特殊的交易稱為利息幣,即持有人可以消耗幣齡獲得利息,同時獲得為網絡產生區塊、以及PoS造幣的優先權。 點點幣的PoS證明計算公式為: 其中proofhash,對應一組數據的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)。 幣齡即bnCoinDayWeight,即幣天,即持有的幣數乘以持有幣的天數,此處天數最大值為90天。 目標值,即bnTarget,用於衡量PoS挖礦難度。目標值與難度成反比,目標值越大、難度越小;反之亦然。 由公式可見,持有的幣天越大,挖到區塊的機會越大。 peercoin-0.6.1ppc中PoS證明計算代碼如下: 點點幣使用目標值來衡量挖礦難度,目標值與難度成反比,目標值越大、難度越小;反之亦然。當前區塊的目標值與前一個區塊目標值、前兩個區塊的時間間隔有關。 計算公式如下:當前區塊目標值 = 前一個區塊目標值 x (1007x10x60 + 2x前兩個區塊時間間隔) / (1009x10x60) 由公式可見,兩個區塊目標間隔時間即為10分鐘。如果前兩個區塊時間間隔大於10分鐘,目標值會提高,即當前區塊難度會降低。反之,如果前兩個區塊時間間隔小於10分鐘,目標值會降低,即當前區塊難度會提高。 peercoin-0.6.1ppc中目標值計算代碼如下: 為了進一步鞏固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證明計算公式為: 其中proofhash,對應一組數據的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)。幣數即nWeight,目標值即bnTarget。 blackcoin-1.2.4中PoS證明計算代碼如下: 在 PoW 機制中,當賬本出現分叉時,對 PoW 這種算力敏感的算法,礦工必須選擇一個方向進行挖礦。而在 PoS 這種算力不敏感的時候,PoS 礦工往往會兩個方向都挖,以爭取實現利益最大化。當多數礦工都在兩條鏈上一起挖礦的時候,就會很容易出現雙重支付攻擊。 作者:黎躍春 孔壹學院創始人兼CEO 黎躍春:孔壹學院創始人兼CEO,國內區塊鏈佈道先行者,通信和信息技術培養工程區塊鏈高級授課專家。 從零到壹學習共識算法為一個系列,一共18講,包括通過一個App的演示深入理解區塊鏈運行原理、go 實現簡單的區塊鏈、拜占庭、PoW、PoS、DPoS、Paxos、Raft等,為大家詳盡的介紹共識算法的學習過程。今天我們將為大家介紹從零到壹學習共識算法第九講:共識機制-權益證明 PoS。話不多說,馬上開啟我們的共識算法學習之旅。 點點幣PPCoin 前期採用PoW挖礦開採和分配貨幣,以保證公平。後期採用PoS機制,保障網絡安全,即擁有51%貨幣難度更大,從而防止51%攻擊。 PoS核心概念為幣齡,即持有貨幣的時間。例如有10個幣、持有90天,即擁有900幣天的幣齡。另外使用幣,即意味著幣齡的銷燬。 在PoS中有一種特殊的交易稱為利息幣,即持有人可以消耗幣齡獲得利息,同時獲得為網絡產生區塊、以及PoS造幣的優先權。 點點幣的PoS證明計算公式為: 其中proofhash,對應一組數據的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)。 幣齡即bnCoinDayWeight,即幣天,即持有的幣數乘以持有幣的天數,此處天數最大值為90天。 目標值,即bnTarget,用於衡量PoS挖礦難度。目標值與難度成反比,目標值越大、難度越小;反之亦然。 由公式可見,持有的幣天越大,挖到區塊的機會越大。 peercoin-0.6.1ppc中PoS證明計算代碼如下: 點點幣使用目標值來衡量挖礦難度,目標值與難度成反比,目標值越大、難度越小;反之亦然。當前區塊的目標值與前一個區塊目標值、前兩個區塊的時間間隔有關。 計算公式如下:當前區塊目標值 = 前一個區塊目標值 x (1007x10x60 + 2x前兩個區塊時間間隔) / (1009x10x60) 由公式可見,兩個區塊目標間隔時間即為10分鐘。如果前兩個區塊時間間隔大於10分鐘,目標值會提高,即當前區塊難度會降低。反之,如果前兩個區塊時間間隔小於10分鐘,目標值會降低,即當前區塊難度會提高。 peercoin-0.6.1ppc中目標值計算代碼如下: 為了進一步鞏固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證明計算公式為: 其中proofhash,對應一組數據的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)。幣數即nWeight,目標值即bnTarget。 blackcoin-1.2.4中PoS證明計算代碼如下: 在 PoW 機制中,當賬本出現分叉時,對 PoW 這種算力敏感的算法,礦工必須選擇一個方向進行挖礦。而在 PoS 這種算力不敏感的時候,PoS 礦工往往會兩個方向都挖,以爭取實現利益最大化。當多數礦工都在兩條鏈上一起挖礦的時候,就會很容易出現雙重支付攻擊。 PoS 機制中,由於持幣量會對挖礦難度產生影響。因此,當一個基於 PoS 體系代幣系統啟動時,就會面臨早期獲得代幣的持有者,沒有動力去花費或者轉移代幣給第三方。同時,持有越多的幣,越容易挖到礦,這樣就產生了代幣初始流通性問題。 解決方案:早起幾個版本,通過 PoW 機制來創建貨幣,而非 PoS。由於 PoW 本身的性質,礦工在挖礦過程中往往需要資金來升級硬件,所以會讓礦工手中的幣流通起來。 PoS 中,產生每個 Block 的速度相對 PoW 快了很多。因此,少數不懷好意的節點會想著把整個區塊鏈共識賬本全部重寫。這在 PoW 中是經典的 51% 問題,即:當某一個節點控制了 51% 及以上算力,就有能力篡改賬本,但達到 51% 算力是件極其困難的事情。而在 PoS 中缺乏對算力的約束,那麼就存在潛在可能篡改賬本。 解決方案:同步時,限制最大能接受的分叉節點數量。 賄賂攻擊流程如下: 攻擊者購買某個商品或服務商戶開始等待網絡確認這筆交易此時,攻擊者開始在網絡中首次宣稱,對目前相對最長的不包含這次交易的主鏈進行獎勵。當主鏈足夠長時,攻擊者開始放出更大的獎勵,獎勵那些在包含此次交易的鏈條中挖礦的礦工。六次確認達成後,放棄獎勵。貨物到手,同時放棄攻擊者選中的鏈條。因此,只要此次賄賂攻擊的成本小於貨物或者服務費用,此次攻擊就是成功的。相比之下,PoW 機制中賄賂攻擊就需要賄賂大多數礦工,因此成本極高,難以實現。 在最早的 Peercoin 版本中,挖礦難度不僅與當前賬戶餘額有關,也與每個幣的持幣時間掛鉤。 H(H(Bprev),A,t) ≤ balance(A)mAge(coins) 這就導致,部分節點在等待足夠長時間後,就有能力利用 Age 的增加來控制整個網絡,產生非常顯著的影響。 解決方案:限制 CoinAge 的最大值。 當 PoS 中的某一節點佔有了一定量的算力後,PoS 中佔有特定算力的節點,就有能力通過控制 H prev來使自己所在算力範圍有能力去計算 H(H(Bprev),A,t) ≤ balance(A)m 節能。不用挖礦,不需要大量耗費電力和能源。更去中心化。首先說,去中心化是相對的。相對於比特幣等PoW類型的加密貨幣,PoS機制的加密貨幣對計算機硬件基本上沒有過高要求,人人可挖礦(獲得利息),不用擔心算力集中導致中心化的出現(單用戶通過購買獲得51%的貨幣量,成本更高),網絡更加安全有保障。避免緊縮。PoW機制的加密貨幣,因為用戶丟失等各種原因,可能導致通貨緊縮,但是PoS機制的加密貨幣按一定的年利率新增貨幣,可以有效避免緊縮出現,保持基本穩定。比特幣之後,很多新幣採用PoS機制,很多采用工作量證明機制的老幣,也紛紛修改協議,“硬分叉”升級為PoS機制。 純PoS機制的加密貨幣,只能通過IPO的方式發行,這就導致“少數人”(通常是開發者)獲得大量成本極低的加密貨幣,在利益面前,很難保證他們不會大量拋售。PoS機制的加密貨幣,信用基礎不夠牢固。為解決這個問題,很多采用PoW+PoS的雙重機制,通過PoW挖礦發行加密貨幣,使用PoS維護網絡穩定。或者採用DPoS機制,通過社區選舉的方式,增強信任。 參考鏈接: http://blog.51cto.com/11821908/2060084 https://daimajia.com/2017/09/14/pow-and-pos https://juejin.im/post/5ac1e9adf265da23906c2dc8proofhash < 幣齡x目標值
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;}//代碼位置 https://github.com/peercoin/peercoin/blob/ppc-0.6-rc1/src/kernel.cpp
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();}//代碼位置 https://github.com/peercoin/peercoin/blob/master/src/main.cp
proofhash < 幣數x目標值hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight
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;}//源碼地址 https://github.com/CoinBlack/blackcoin/blob/master/src/kernel.cpp
proofhash < 幣齡x目標值
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;}//代碼位置 https://github.com/peercoin/peercoin/blob/ppc-0.6-rc1/src/kernel.cpp
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();}//代碼位置 https://github.com/peercoin/peercoin/blob/master/src/main.cp
proofhash < 幣數x目標值hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight
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;}//源碼地址 https://github.com/CoinBlack/blackcoin/blob/master/src/kernel.cpp
冷啟動問題 ( Initial Distribution Problem )
長距離攻擊 ( Long-Range Attack )
賄賂攻擊(Bribe Attack)
幣齡加和攻擊 ( Coin Age Accumulation Attack )
預計算攻擊 ( Precomputing Attack)
優點
缺點