Untitled diff

Created Diff never expires
147 removals
398 lines
206 additions
456 lines
// Copyright (c) 2012-2013 The PPCoin developers
// Copyright (c) 2012-2013 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// file COPYING or http://www.opensource.org/licenses/mit-license.php.


#include <boost/assign/list_of.hpp>
#include <boost/assign/list_of.hpp>


#include "kernel.h"
#include "kernel.h"
#include "txdb.h"
#include "db.h"


using namespace std;
using namespace std;


extern int nStakeMaxAge;
extern int nStakeTargetSpacing;

// Modifier interval: time to elapse before new modifier is computed
// Modifier interval: time to elapse before new modifier is computed
// Set to 6-hour for production network and 20-minute for test network
// Set to 3-hour for production network and 20-minute for test network
unsigned int nModifierInterval = MODIFIER_INTERVAL;

unsigned int nModifierInterval;
unsigned int getIntervalVersion(bool fTestNet)
{
if(fTestNet)
return MODIFIER_INTERVAL_TESTNET;
else
return MODIFIER_INTERVAL;
}


// Hard checkpoints of stake modifiers to ensure they are deterministic
// Hard checkpoints of stake modifiers to ensure they are deterministic
static std::map<int, unsigned int> mapStakeModifierCheckpoints =
static std::map<int, unsigned int> mapStakeModifierCheckpoints =
boost::assign::map_list_of
boost::assign::map_list_of
( 0, 0x0e00670bu )
( 0, 0xfd11f4e7u )
( 261031, 0x08c12bdb8 )
;
( 435735, 0x0b956cfa0 )
// Get time weight
int64 GetWeight(int64 nIntervalBeginning, int64 nIntervalEnd)
{
return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64)nStakeMaxAge);
}


;
// Get time weight 2 - This is added for informational purposes since staking takes 8.8 days min approx. because of bug

int64 GetWeight2(int64 nIntervalBeginning, int64 nIntervalEnd)
int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd)
{
{
// Kernel hash weight starts from 0 at the 10-day min age
int64 nTimePassed = nIntervalEnd - nIntervalBeginning;
// this change increases active coins participating the hash and helps
return min(nTimePassed, (int64)nStakeMaxAge) - nStakeMinAge;
// to secure the network when proof-of-stake difficulty is low
//
// Maximum TimeWeight is 30 days.

if ( nIntervalEnd > VERSION1_5_SWITCH_TIME )
return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge);
else
return min(nIntervalEnd - nIntervalBeginning, (int64_t)nStakeMaxAge) - nStakeMinAge;
}
}


// Get the last stake modifier and its generation time from a given block
// Get the last stake modifier and its generation time from a given block
static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime)
static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64& nStakeModifier, int64& nModifierTime)
{
{
if (!pindex)
if (!pindex)
return error("GetLastStakeModifier: null pindex");
return error("GetLastStakeModifier: null pindex");
while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
pindex = pindex->pprev;
pindex = pindex->pprev;
if (!pindex->GeneratedStakeModifier())
if (!pindex->GeneratedStakeModifier())
return error("GetLastStakeModifier: no generation at genesis block");
return error("GetLastStakeModifier: no generation at genesis block");
nStakeModifier = pindex->nStakeModifier;
nStakeModifier = pindex->nStakeModifier;
nModifierTime = pindex->GetBlockTime();
nModifierTime = pindex->GetBlockTime();
return true;
return true;
}
}


// Get selection interval section (in seconds)
// Get selection interval section (in seconds)
static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
static int64 GetStakeModifierSelectionIntervalSection(int nSection)
{
{
assert (nSection >= 0 && nSection < 64);
assert (nSection >= 0 && nSection < 64);
return (nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))));
int64 a = getIntervalVersion(fTestNet) * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)));
return a;
}
}


// Get stake modifier selection interval (in seconds)
// Get stake modifier selection interval (in seconds)
static int64_t GetStakeModifierSelectionInterval()
static int64 GetStakeModifierSelectionInterval()
{
{
int64_t nSelectionInterval = 0;
int64 nSelectionInterval = 0;
for (int nSection=0; nSection<64; nSection++)
for (int nSection=0; nSection<64; nSection++)
{
nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
}
return nSelectionInterval;
return nSelectionInterval;
}
}


// select a block from the candidate blocks in vSortedByTimestamp, excluding
// select a block from the candidate blocks in vSortedByTimestamp, excluding
// already selected blocks in vSelectedBlocks, and with timestamp up to
// already selected blocks in vSelectedBlocks, and with timestamp up to
// nSelectionIntervalStop.
// nSelectionIntervalStop.
static bool SelectBlockFromCandidates(
static bool SelectBlockFromCandidates(
vector<pair<int64_t, uint256> >& vSortedByTimestamp,
vector<pair<int64, uint256> >& vSortedByTimestamp,
map<uint256, const CBlockIndex*>& mapSelectedBlocks,
map<uint256, const CBlockIndex*>& mapSelectedBlocks,
int64_t nSelectionIntervalStop, uint64_t nStakeModifierPrev,
int64 nSelectionIntervalStop, uint64 nStakeModifierPrev,
const CBlockIndex** pindexSelected)
const CBlockIndex** pindexSelected)
{
{
bool fSelected = false;
bool fSelected = false;
uint256 hashBest = 0;
uint256 hashBest = 0;
*pindexSelected = (const CBlockIndex*) 0;
*pindexSelected = (const CBlockIndex*) 0;
BOOST_FOREACH(const PAIRTYPE(int64_t, uint256)& item, vSortedByTimestamp)
BOOST_FOREACH(const PAIRTYPE(int64, uint256)& item, vSortedByTimestamp)
{
{
if (!mapBlockIndex.count(item.second))
if (!mapBlockIndex.count(item.second))
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str());
const CBlockIndex* pindex = mapBlockIndex[item.second];
const CBlockIndex* pindex = mapBlockIndex[item.second];
if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
break;
break;
if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
continue;
continue;
// compute the selection hash by hashing its proof-hash and the
// compute the selection hash by hashing its proof-hash and the
// previous proof-of-stake modifier
// previous proof-of-stake modifier
uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
CDataStream ss(SER_GETHASH, 0);
CDataStream ss(SER_GETHASH, 0);
ss << hashProof << nStakeModifierPrev;
ss << hashProof << nStakeModifierPrev;
uint256 hashSelection = Hash(ss.begin(), ss.end());
uint256 hashSelection = Hash(ss.begin(), ss.end());
// the selection hash is divided by 2**32 so that proof-of-stake block
// the selection hash is divided by 2**32 so that proof-of-stake block
// is always favored over proof-of-work block. this is to preserve
// is always favored over proof-of-work block. this is to preserve
// the energy efficiency property
// the energy efficiency property
if (pindex->IsProofOfStake())
if (pindex->IsProofOfStake())
hashSelection >>= 32;
hashSelection >>= 32;
if (fSelected && hashSelection < hashBest)
if (fSelected && hashSelection < hashBest)
{
{
hashBest = hashSelection;
hashBest = hashSelection;
*pindexSelected = (const CBlockIndex*) pindex;
*pindexSelected = (const CBlockIndex*) pindex;
}
}
else if (!fSelected)
else if (!fSelected)
{
{
fSelected = true;
fSelected = true;
hashBest = hashSelection;
hashBest = hashSelection;
*pindexSelected = (const CBlockIndex*) pindex;
*pindexSelected = (const CBlockIndex*) pindex;
}
}
}
}
LogPrint("stakemodifier", "SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString());
if (fDebug && GetBoolArg("-printstakemodifier"))
printf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString().c_str());
return fSelected;
return fSelected;
}
}


// Stake Modifier (hash modifier of proof-of-stake):
// Stake Modifier (hash modifier of proof-of-stake):
// The purpose of stake modifier is to prevent a txout (coin) owner from
// The purpose of stake modifier is to prevent a txout (coin) owner from
// computing future proof-of-stake generated by this txout at the time
// computing future proof-of-stake generated by this txout at the time
// of transaction confirmation. To meet kernel protocol, the txout
// of transaction confirmation. To meet kernel protocol, the txout
// must hash with a future stake modifier to generate the proof.
// must hash with a future stake modifier to generate the proof.
// Stake modifier consists of bits each of which is contributed from a
// Stake modifier consists of bits each of which is contributed from a
// selected block of a given block group in the past.
// selected block of a given block group in the past.
// The selection of a block is based on a hash of the block's proof-hash and
// The selection of a block is based on a hash of the block's proof-hash and
// the previous stake modifier.
// the previous stake modifier.
// Stake modifier is recomputed at a fixed time interval instead of every
// Stake modifier is recomputed at a fixed time interval instead of every
// block. This is to make it difficult for an attacker to gain control of
// block. This is to make it difficult for an attacker to gain control of
// additional bits in the stake modifier, even after generating a chain of
// additional bits in the stake modifier, even after generating a chain of
// blocks.
// blocks.
bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier)
bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModifier, bool& fGeneratedStakeModifier)
{
{
nStakeModifier = 0;
nStakeModifier = 0;
fGeneratedStakeModifier = false;
fGeneratedStakeModifier = false;
if (!pindexPrev)
if (!pindexPrev)
{
{
fGeneratedStakeModifier = true;
fGeneratedStakeModifier = true;
return true; // genesis block's modifier is 0
return true; // genesis block's modifier is 0
}
}

// First find current stake modifier and its generation block time
// First find current stake modifier and its generation block time
// if it's not old enough, return the same stake modifier
// if it's not old enough, return the same stake modifier
int64_t nModifierTime = 0;
int64 nModifierTime = 0;
if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
return error("ComputeNextStakeModifier: unable to get last modifier");
return error("ComputeNextStakeModifier: unable to get last modifier");

if (fDebug)
LogPrint("stakemodifier", "ComputeNextStakeModifier: prev modifier=0x%016x time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime));
printf("ComputeNextStakeModifier: prev modifier=0x%016"PRI64x" time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime).c_str());

if (nModifierTime / getIntervalVersion(fTestNet) >= pindexPrev->GetBlockTime() / getIntervalVersion(fTestNet))
if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval)
return true;
return true;


// Sort candidate blocks by timestamp
// Sort candidate blocks by timestamp
vector<pair<int64_t, uint256> > vSortedByTimestamp;
vector<pair<int64, uint256> > vSortedByTimestamp;
vSortedByTimestamp.reserve(64 * nModifierInterval / nStakeTargetSpacing);
vSortedByTimestamp.reserve(64 * getIntervalVersion(fTestNet) / nStakeTargetSpacing);
int64_t nSelectionInterval = GetStakeModifierSelectionInterval();
int64 nSelectionInterval = GetStakeModifierSelectionInterval();
int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / nModifierInterval) * nModifierInterval - nSelectionInterval;
int64 nSelectionIntervalStart = (pindexPrev->GetBlockTime() / getIntervalVersion(fTestNet)) * getIntervalVersion(fTestNet) - nSelectionInterval;
const CBlockIndex* pindex = pindexPrev;
const CBlockIndex* pindex = pindexPrev;
while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
{
{
vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash()));
vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash()));
pindex = pindex->pprev;
pindex = pindex->pprev;
}
}

int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end());


// Select 64 blocks from candidate blocks to generate stake modifier
// Select 64 blocks from candidate blocks to generate stake modifier
uint64_t nStakeModifierNew = 0;
uint64 nStakeModifierNew = 0;
int64_t nSelectionIntervalStop = nSelectionIntervalStart;
int64 nSelectionIntervalStop = nSelectionIntervalStart;
map<uint256, const CBlockIndex*> mapSelectedBlocks;
map<uint256, const CBlockIndex*> mapSelectedBlocks;
for (int nRound=0; nRound<min(64, (int)vSortedByTimestamp.size()); nRound++)
for (int nRound=0; nRound<min(64, (int)vSortedByTimestamp.size()); nRound++)
{
{
// add an interval section to the current selection round
// add an interval section to the current selection round
nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
// select a block from the candidates of current round
// select a block from the candidates of current round
if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex))
if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex))
return error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
return error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
// write the entropy bit of the selected block
// write the entropy bit of the selected block
nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
nStakeModifierNew |= (((uint64)pindex->GetStakeEntropyBit()) << nRound);
// add the selected block from candidates to selected list
// add the selected block from candidates to selected list
mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
LogPrint("stakemodifier", "ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n",
if (fDebug && GetBoolArg("-printstakemodifier"))
nRound, DateTimeStrFormat(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit());
printf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n",
nRound, DateTimeStrFormat(nSelectionIntervalStop).c_str(), pindex->nHeight, pindex->GetStakeEntropyBit());
}
}


// Print selection map for visualization of the selected blocks
// Print selection map for visualization of the selected blocks
if (LogAcceptCategory("stakemodifier"))
if (fDebug && GetBoolArg("-printstakemodifier"))
{
{
string strSelectionMap = "";
string strSelectionMap = "";
// '-' indicates proof-of-work blocks not selected
// '-' indicates proof-of-work blocks not selected
strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
pindex = pindexPrev;
pindex = pindexPrev;
while (pindex && pindex->nHeight >= nHeightFirstCandidate)
while (pindex && pindex->nHeight >= nHeightFirstCandidate)
{
{
// '=' indicates proof-of-stake blocks not selected
// '=' indicates proof-of-stake blocks not selected
if (pindex->IsProofOfStake())
if (pindex->IsProofOfStake())
strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
pindex = pindex->pprev;
pindex = pindex->pprev;
}
}
BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks)
BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks)
{
{
// 'S' indicates selected proof-of-stake blocks
// 'S' indicates selected proof-of-stake blocks
// 'W' indicates selected proof-of-work blocks
// 'W' indicates selected proof-of-work blocks
strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
}
}
LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap);
printf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str());
}
if (fDebug)
{
printf("ComputeNextStakeModifier: new modifier=0x%016"PRI64x" time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()).c_str());
}
}
LogPrint("stakemodifier", "ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()));


nStakeModifier = nStakeModifierNew;
nStakeModifier = nStakeModifierNew;
fGeneratedStakeModifier = true;
fGeneratedStakeModifier = true;
return true;
return true;
}
}


// The stake modifier used to hash for a stake kernel is chosen as the stake
// The stake modifier used to hash for a stake kernel is chosen as the stake
// modifier about a selection interval later than the coin generating the kernel
// modifier about a selection interval later than the coin generating the kernel
static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64& nStakeModifier, int& nStakeModifierHeight, int64& nStakeModifierTime, bool fPrintProofOfStake)
{
{
nStakeModifier = 0;
nStakeModifier = 0;
if (!mapBlockIndex.count(hashBlockFrom))
if (!mapBlockIndex.count(hashBlockFrom))
return error("GetKernelStakeModifier() : block not indexed");
return error("GetKernelStakeModifier() : block not indexed");
const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom];
const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom];
nStakeModifierHeight = pindexFrom->nHeight;
nStakeModifierHeight = pindexFrom->nHeight;
nStakeModifierTime = pindexFrom->GetBlockTime();
nStakeModifierTime = pindexFrom->GetBlockTime();
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
int64 nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
const CBlockIndex* pindex = pindexFrom;
const CBlockIndex* pindex = pindexFrom;

// loop to find the stake modifier later by a selection interval
// loop to find the stake modifier later by a selection interval
while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
{
{
if (!pindex->pnext)
if (!pindex->pnext)
{ // reached best block; may happen if node is behind on block chain
{ // reached best block; may happen if node is behind on block chain
if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
pindex->GetBlockHash().ToString(), pindex->nHeight, hashBlockFrom.ToString());
pindex->GetBlockHash().ToString().c_str(), pindex->nHeight, hashBlockFrom.ToString().c_str());
else
else
return false;
{
//printf("FAILED BECAUSE no pindexnext\n");
return false;
}
}
}
pindex = pindex->pnext;
pindex = pindex->pnext;
if (pindex->GeneratedStakeModifier())
if (pindex->GeneratedStakeModifier())
{
{
nStakeModifierHeight = pindex->nHeight;
nStakeModifierHeight = pindex->nHeight;
nStakeModifierTime = pindex->GetBlockTime();
nStakeModifierTime = pindex->GetBlockTime();
}
}
}
}
nStakeModifier = pindex->nStakeModifier;
nStakeModifier = pindex->nStakeModifier;
return true;
return true;
}
}


// ppcoin kernel protocol
// ppcoin kernel protocol
// coinstake must meet hash target according to the protocol:
// coinstake must meet hash target according to the protocol:
// kernel (input 0) must meet the formula
// kernel (input 0) must meet the formula
// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight
// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight
// this ensures that the chance of getting a coinstake is proportional to the
// this ensures that the chance of getting a coinstake is proportional to the
// amount of coin age one owns.
// amount of coin age one owns.
// The reason this hash is chosen is the following:
// The reason this hash is chosen is the following:
// nStakeModifier:
// nStakeModifier:
// (v0.3) scrambles computation to make it very difficult to precompute
// (v0.3) scrambles computation to make it very difficult to precompute
// future proof-of-stake at the time of the coin's confirmation
// future proof-of-stake at the time of the coin's confirmation
// (v0.2) nBits (deprecated): encodes all past block timestamps
// (v0.2) nBits (deprecated): encodes all past block timestamps
// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
// generate transaction for future advantage
// generate transaction for future advantage
// txPrev.offset: offset of txPrev inside block, to reduce the chance of
// txPrev.offset: offset of txPrev inside block, to reduce the chance of
// nodes generating coinstake at the same time
// nodes generating coinstake at the same time
// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
// time
// time

// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
// generating coinstake at the same time
// generating coinstake at the same time
// block/tx hash should not be used here as they can be generated in vast
// block/tx hash should not be used here as they can be generated in vast
// quantities so as to generate blocks faster, degrading the system back into
// quantities so as to generate blocks faster, degrading the system back into
// a proof-of-work situation.
// a proof-of-work situation.
//
//
bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, 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");

unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
return error("CheckStakeKernelHash() : min age violation");


CBigNum bnTargetPerCoinDay;
/** Presstab - HyperStake hashing
bnTargetPerCoinDay.SetCompact(nBits);
I redesigned the hashing iteration in a few ways.
int64_t nValueIn = txPrev.vout[prevout.n].nValue;
Code Reorginization - Instead of iterating the hashing in wallet.cpp, it is iterated in kernel.cpp inside of checkstakekernelhash, this allows

the iteration to not need to initialize the variables for every iteration. This is also true for the stake modifier, which was previously
uint256 hashBlockFrom = blockFrom.GetHash();
calculated for each iteration.

liteStake - Previously the staking process would continuosly rehash the same hashes over and over, needlessly taking up valuable CPU power.
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60);
I have added a std::map that tracks the block height and the last time the wallet hashed on this height. Depending on your staking settings,
targetProofOfStake = CBigNum(bnCoinDayWeight * bnTargetPerCoinDay).getuint256();
the wallet will not begin a new round of hashing until after a certain amount of time has passed, or a new block is accepted. This time delay

can be found in main.cpp bitcoinminer(). This means that there will be 1-5 seconds of hashing with the CPU once every few minutes, compared to
// Calculate hash
continued hashing with the CPU.
CDataStream ss(SER_GETHASH, 0);
**/
uint64_t nStakeModifier = 0;
uint256 stakeHash(unsigned int nTimeTx, unsigned int nTxPrevTime, CDataStream ss, unsigned int prevoutIndex, unsigned int nTxPrevOffset, unsigned int nTimeBlockFrom)
int nStakeModifierHeight = 0;
{
int64_t nStakeModifierTime = 0;
ss << nTimeBlockFrom << nTxPrevOffset << nTxPrevTime << prevoutIndex << nTimeTx;
return Hash(ss.begin(), ss.end());
}


if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
//HyperStake test hash vs target
return false;
bool stakeTargetHit(uint256 hashProofOfStake, unsigned int nAge, int64 nValueIn, CBigNum bnTargetPerCoinDay)
ss << nStakeModifier;
{
//get the stake weight
int64 nTimeWeight = min((int64)nAge, (int64)nStakeMaxAge) - nStakeMinAge;
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
// Now check if proof-of-stake hash meets target protocol
return (CBigNum(hashProofOfStake) < bnCoinDayWeight * bnTargetPerCoinDay);
}


ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
//instead of looping outside and reinitializing variables many times, we will give a nTimeTx and also search interval so that we can do all the hashing here
hashProofOfStake = Hash(ss.begin(), ss.end());
bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev,
if (fPrintProofOfStake)
const COutPoint& prevout, unsigned int& nTimeTx, unsigned int nHashDrift, bool fCheck, uint256& hashProofOfStake, bool fPrintProofOfStake)
{
{
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
//assign new variables to make it easier to read
nStakeModifier, nStakeModifierHeight,
int64 nValueIn = txPrev.vout[prevout.n].nValue;
DateTimeStrFormat(nStakeModifierTime),
unsigned int nTxPrevTime = txPrev.nTime;
mapBlockIndex[hashBlockFrom]->nHeight,
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
DateTimeStrFormat(blockFrom.GetBlockTime()));
LogPrintf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
if (nTimeTx < txPrev.nTime) // Transaction timestamp violation
"0.3",
return error("CheckStakeKernelHash() : nTime violation");
nStakeModifier,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
hashProofOfStake.ToString());
return error("CheckStakeKernelHash() : min age violation");
}
//grab difficulty
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
//grab stake modifier - HyperStake improves hashing by only grabbing this once per utxo
uint64 nStakeModifier = 0;
int nStakeModifierHeight = 0;
int64 nStakeModifierTime = 0;
if (!GetKernelStakeModifier(blockFrom.GetHash(), nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
return false;
//create data stream once instead of repeating it in the loop
CDataStream ss(SER_GETHASH, 0);
ss << nStakeModifier;
//if wallet is simply checking to make sure a hash is valid
if(fCheck)
{
hashProofOfStake = stakeHash(nTimeTx, nTxPrevTime, ss, prevout.n, nTxPrevOffset, nTimeBlockFrom);
return stakeTargetHit(hashProofOfStake, (int64)nTimeTx - nTxPrevTime, nValueIn, bnTargetPerCoinDay);
}
bool fSuccess = false;
unsigned int nTryTime = 0;
unsigned int i;
for(i = 0; i < (nHashDrift); i++) //iterate the hashing
{
//hash this iteration
nTryTime = nTimeTx + nHashDrift - i;
hashProofOfStake = stakeHash(nTryTime, nTxPrevTime, ss, prevout.n, nTxPrevOffset, nTimeBlockFrom);


// Now check if proof-of-stake hash meets target protocol
// if stake hash does not meet the target then continue to next iteration
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
if(!stakeTargetHit(hashProofOfStake, (int64)nTimeTx - nTxPrevTime, nValueIn, bnTargetPerCoinDay))
return false;
continue;
if (fDebug && !fPrintProofOfStake)
{
fSuccess = true; // if we make it this far then we have successfully created a stake hash
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nTimeTx = nTryTime;
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime),
if (fDebug || fPrintProofOfStake)
mapBlockIndex[hashBlockFrom]->nHeight,
{
DateTimeStrFormat(blockFrom.GetBlockTime()));
printf("CheckStakeKernelHash() : using modifier 0x%016"PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
LogPrintf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier, nStakeModifierHeight,
"0.3",
DateTimeStrFormat(nStakeModifierTime).c_str(),
nStakeModifier,
mapBlockIndex[blockFrom.GetHash()]->nHeight,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
hashProofOfStake.ToString());
printf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016"PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
}
"0.3",
return true;
nStakeModifier,
nTimeBlockFrom, nTxPrevOffset, nTxPrevTime, prevout.n, nTryTime,
hashProofOfStake.ToString().c_str());
}
}
mapHashedBlocks.clear();
mapHashedBlocks[nBestHeight] = GetTime(); //store a time stamp of when we last hashed on this block
return fSuccess;
}
}


// Check kernel hash target and coinstake signature
// Check kernel hash target and coinstake signature
bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake)
bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake)
{
{
if (!tx.IsCoinStake())
if (!tx.IsCoinStake())
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString());
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str());


// Kernel (input 0) must match the stake hash target per coin age (nBits)
// Kernel (input 0) must match the stake hash target per coin age (nBits)
const CTxIn& txin = tx.vin[0];
const CTxIn& txin = tx.vin[0];


// First try finding the previous transaction in database
// First try finding the previous transaction in database
CTxDB txdb("r");
CTxDB txdb("r");
CTransaction txPrev;
CTransaction txPrev;
CTxIndex txindex;
CTxIndex txindex;
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download
return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download
#ifndef USE_LEVELDB
txdb.Close();
txdb.Close();

#endif
// Verify signature
// Verify signature
if (!VerifySignature(txPrev, tx, 0, 0))
if (!VerifySignature(txPrev, tx, 0, true, 0))
return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString()));
return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str()));


// Read block header
// Read block header
CBlock block;
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction
return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction


if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug))
unsigned int nInterval = 0;
return tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync
unsigned int nTxTime = tx.nTime;
if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, txPrev, txin.prevout, nTxTime, nInterval, true, hashProofOfStake, fDebug))
return tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str())); // may occur during initial download or if behind on block chain sync


return true;
return true;
}
}


// Check whether the coinstake timestamp meets protocol
// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx)
bool CheckCoinStakeTimestamp(int64 nTimeBlock, int64 nTimeTx)
{
{
// v0.3 protocol
// v0.3 protocol
return (nTimeBlock == nTimeTx);
return (nTimeBlock == nTimeTx);
}
}


// Get stake modifier checksum
// Get stake modifier checksum
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex, bool fLoad)
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
{
{
assert (pindex->pprev || pindex->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
assert (pindex->pprev || pindex->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
// Hash previous checksum with flags, hashProofOfStake and nStakeModifier
// Hash previous checksum with flags, hashProofOfStake and nStakeModifier
CDataStream ss(SER_GETHASH, 0);
CDataStream ss(SER_GETHASH, 0);
if (pindex->pprev)
if (pindex->pprev)
ss << pindex->pprev->nStakeModifierChecksum;
ss << pindex->pprev->nStakeModifierChecksum;
ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
uint256 hashChecksum = Hash(ss.begin(), ss.end());
uint256 hashChecksum = Hash(ss.begin(), ss.end());
hashChecksum >>= (256 - 32);
hashChecksum >>= (256 - 32);
if(!fLoad)
LogPrint("stakechecksum", "GetStakeModifierChecksum: StakeCheckSum=0x%09x, at height=%d\n",
hashChecksum.Get64(), pindex->nHeight );
return hashChecksum.Get64();
return hashChecksum.Get64();
}
}


// Check stake modifier hard checkpoints
// Check stake modifier hard checkpoints
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
{
{
if (fTestNet) return true; // Testnet has no checkpoints
if (fTestNet) return true; // Testnet has no checkpoints
if (mapStakeModifierCheckpoints.count(nHeight))
if (mapStakeModifierCheckpoints.count(nHeight))
{
return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];
return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];
}
return true;
return true;
}
}