Skip to content

Commit

Permalink
liteStake
Browse files Browse the repository at this point in the history
  • Loading branch information
presstab committed Apr 13, 2015
1 parent 2c26fbb commit 6d6f887
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 142 deletions.
155 changes: 97 additions & 58 deletions src/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,69 +285,106 @@ static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifi
// quantities so as to generate blocks faster, degrading the system back into
// 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)
/** Presstab - HyperStake hashing
I redesigned the hashing iteration in a few ways.
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
calculated for each iteration.
liteStake - Previously the staking process would continuosly rehash the same hashes over and over, needlessly taking up valuable CPU power.
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,
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
continued hashing with the CPU.
**/
uint256 stakeHash(unsigned int nTimeTx, unsigned int nTxPrevTime, CDataStream ss, unsigned int prevoutIndex, unsigned int nTxPrevOffset, unsigned int nTimeBlockFrom)
{
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;
bnTargetPerCoinDay.SetCompact(nBits);
int64_t nValueIn = txPrev.vout[prevout.n].nValue;

uint256 hashBlockFrom = blockFrom.GetHash();

CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60);
targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256();

// Calculate hash
CDataStream ss(SER_GETHASH, 0);
uint64_t nStakeModifier = 0;
int nStakeModifierHeight = 0;
int64_t nStakeModifierTime = 0;

if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
return false;
ss << nStakeModifier;
ss << nTimeBlockFrom << nTxPrevOffset << nTxPrevTime << prevoutIndex << nTimeTx;
return Hash(ss.begin(), ss.end());
}

ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
hashProofOfStake = Hash(ss.begin(), ss.end());
if (fPrintProofOfStake)
{
printf("CheckStakeKernelHash() : using modifier 0x%016"PRIx64" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime).c_str(),
mapBlockIndex[hashBlockFrom]->nHeight,
DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
printf("CheckStakeKernelHash() : check modifier=0x%016"PRIx64" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString().c_str());
}
//HyperStake test hash vs target
bool stakeTargetHit(uint256 hashProofOfStake, int64_t nTimeWeight, int64_t nValueIn, CBigNum bnTargetPerCoinDay)
{
//get the stake weight
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);

// Now check if proof-of-stake hash meets target protocol
return (CBigNum(hashProofOfStake) < bnCoinDayWeight * bnTargetPerCoinDay);
}

// Now check if proof-of-stake hash meets target protocol
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
return false;
if (fDebug && !fPrintProofOfStake)
{
printf("CheckStakeKernelHash() : using modifier 0x%016"PRIx64" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
DateTimeStrFormat(nStakeModifierTime).c_str(),
mapBlockIndex[hashBlockFrom]->nHeight,
DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
printf("CheckStakeKernelHash() : pass modifier=0x%016"PRIx64" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier,
nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
hashProofOfStake.ToString().c_str());
}
return true;
//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
bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev,
const COutPoint& prevout, unsigned int& nTimeTx, unsigned int nHashDrift, bool fCheck, uint256& hashProofOfStake, bool fPrintProofOfStake)
{
//assign new variables to make it easier to read
int64_t nValueIn = txPrev.vout[prevout.n].nValue;
unsigned int nTxPrevTime = txPrev.nTime;
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();

if (nTimeTx < txPrev.nTime) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");

if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
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_t nStakeModifier = 0;
int nStakeModifierHeight = 0;
int64_t 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, GetWeight((int64_t)nTxPrevTime, (int64_t)nTimeTx), 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);

// if stake hash does not meet the target then continue to next iteration
if(!stakeTargetHit(hashProofOfStake, GetWeight((int64_t)nTxPrevTime, (int64_t)nTimeTx), nValueIn, bnTargetPerCoinDay))
continue;

fSuccess = true; // if we make it this far then we have successfully created a stake hash
nTimeTx = nTryTime;
if (fDebug || fPrintProofOfStake)
{
printf("CheckStakeKernelHash() : using modifier 0x%016"PRIx64" 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"PRIx64" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
"0.3",
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
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())
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str());
Expand All @@ -371,7 +408,9 @@ bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hash
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

if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug))
unsigned int nInterval = 0;
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;
Expand Down
7 changes: 5 additions & 2 deletions src/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod

// Check whether stake kernel meets hash target
// Sets hashProofOfStake on success return
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=false);
bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev,
const COutPoint& prevout, unsigned int& nTimeTx, unsigned int nInterval, bool fCheck, uint256& hashProofOfStake, bool fPrintProofOfStake=false);
uint256 stakeHash(unsigned int nTimeTx, unsigned int nTxPrevTime, CDataStream ss, unsigned int prevoutIndex, unsigned int nTxPrevOffset, unsigned int nTimeBlockFrom);
bool stakeTargetHit(uint256 hashProofOfStake, int64_t nTimeWeight, int64_t nValueIn, CBigNum bnTargetPerCoinDay);

// Check kernel hash target and coinstake signature
// Sets hashProofOfStake on success return
bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake);
bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake);

// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx);
Expand Down
8 changes: 5 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;

map<uint256, CTransaction> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
map<unsigned int, unsigned int> mapHashedBlocks; // for liteStake

// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
Expand Down Expand Up @@ -2286,10 +2287,11 @@ bool CBlock::AcceptBlock()
return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight));

// Verify hash target and signature of coinstake tx
uint256 hashProofOfStake = 0, targetProofOfStake = 0;
uint256 hashProofOfStake = 0;
if (IsProofOfStake())
{
if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake, targetProofOfStake))

if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake))
{
printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
return false; // do not error here as we expect this during initial block download
Expand Down Expand Up @@ -2502,7 +2504,7 @@ bool CBlock::SignBlock(CWallet& wallet, int64_t nFees)
{
if (txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime())))
{
// make sure coinstake would meet timestamp protocol
// make sure coinstake would meet timestamp protocol
// as it would be the same as the block timestamp
vtx[0].nTime = nTime = txCoinStake.nTime;
nTime = max(pindexBest->GetPastTimeLimit()+1, GetMaxTransactionTime());
Expand Down
1 change: 1 addition & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
extern unsigned char pchMessageStart[4];
extern std::map<uint256, CBlock*> mapOrphanBlocks;
extern std::map<unsigned int, unsigned int> mapHashedBlocks; // for liteStake

// Settings
extern int64_t nTransactionFee;
Expand Down
15 changes: 12 additions & 3 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,18 +485,18 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)

bool CheckStake(CBlock* pblock, CWallet& wallet)
{
uint256 proofHash = 0, hashTarget = 0;
uint256 proofHash = 0;
uint256 hashBlock = pblock->GetHash();

if(!pblock->IsProofOfStake())
return error("CheckStake() : %s is not a proof-of-stake block", hashBlock.GetHex().c_str());

// verify hash target and signature of coinstake tx
if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash, hashTarget))
if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash))
return error("CheckStake() : proof-of-stake checking failed");

//// debug print
printf("CheckStake() : new proof-of-stake block found \n hash: %s \nproofhash: %s \ntarget: %s\n", hashBlock.GetHex().c_str(), proofHash.GetHex().c_str(), hashTarget.GetHex().c_str());
printf("CheckStake() : new proof-of-stake block found \n hash: %s \nproofhash: %s \n", hashBlock.GetHex().c_str(), proofHash.GetHex().c_str());
pblock->print();
printf("out %s\n", FormatMoney(pblock->vtx[1].GetValueOut()).c_str());

Expand Down Expand Up @@ -560,6 +560,15 @@ void StakeMiner(CWallet *pwallet)
continue;
}
}

if(mapHashedBlocks.count(nBestHeight)) //search our map of hashed blocks, see if bestblock has been hashed yet
{
if(GetTime() - mapHashedBlocks[nBestHeight] < min((int)(pwallet->nHashDrift * 0.5), 180)) // wait half of the nHashDrift with max wait of 3 minutes
{
MilliSleep(2500); // 2.5 second sleep
continue;
}
}

//
// Create new block
Expand Down
Loading

0 comments on commit 6d6f887

Please sign in to comment.