Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,18 +278,33 @@ bool stakeTargetHit(uint256 hashProofOfStake, int64_t nValueIn, uint256 bnTarget

//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, 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
//Get stake input amount (output amount of a previous tx)
int64_t nValueIn = txPrev.vout[prevout.n].nValue;
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime(); // When was the block of the tx we've staked created?

// Ensure we can't stake a tx on same block or any blocks before that
if (nTimeTx < nTimeBlockFrom) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");

// Consensus implementation of protocol change.
// Default requirement for stake is nStakeMinAge (1 * 60 * 60 for BWK)
unsigned int nMinStakeAge = nStakeMinAge;

// IF spork is enabled (check "spork active") the requiremnt will be changed (12 * 60 * 60 for BWK)
if (IsSporkActive(SPORK_23_STAKING_REQUIREMENTS) && nTimeBlockFrom >= GetSporkValue(SPORK_23_STAKING_REQUIREMENTS)) {
nMinStakeAge = nStakeMinAgeConsensus;
}

// Bulwark's "Re-Stake". Because Bulwark stores metadata identifying stake in tx we know that previously this was a POS reward
// If you have not previously staked on this input then you will have to wait longer for your stake to mature.
// This penalizes "Stake Grinding" and gives reason to leave stakes alone reducing traffic on the network.
if (IsSporkActive(SPORK_25_BWK_RESTAKE) && nTimeBlockFrom >= GetSporkValue(SPORK_25_BWK_RESTAKE)) {
// Make sure this was a basic stake with no splitthreshold (vout[0]=0 nonstandard,vout[1/2]=pos/mn)
if (!txPrev.IsCoinStake() || txPrev.vout.size() != 3) {
nMinStakeAge *= 2;
}
}

// Ensure at least nMinStakeAge passed between tx and tx with stake
if (nTimeBlockFrom + nMinStakeAge > nTimeTx) // Min age requirement
return error("CheckStakeKernelHash() : min age violation - nTimeBlockFrom=%d nStakeMinAge=%d nTimeTx=%d", nTimeBlockFrom, nMinStakeAge, nTimeTx);

Expand Down
49 changes: 36 additions & 13 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ int GetIXConfirmations(uint256 nTXHash) {
return 0;
}

//@todo This function doesn't seem to be used anywhere, remove?
// ppcoin: total coin age spent in transaction, in the unit of coin-days.
// Only those coins meeting minimum age requirement counts. As those
// transactions not in main chain are not currently indexed so we
Expand All @@ -915,6 +916,7 @@ int GetIXConfirmations(uint256 nTXHash) {
// introduced to help nodes establish a consistent view of the coin
// age (trust score) of competing branches.
bool GetCoinAge(const CTransaction& tx, const unsigned int nTxTime, uint64_t& nCoinAge) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, trailing space

uint256 bnCentSecond = 0; // coin age in the unit of cent-seconds
nCoinAge = 0;

Expand Down Expand Up @@ -4225,41 +4227,62 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo

if (block.IsProofOfStake()) {
// Coinbase output should be empty if proof-of-stake block
// The way POS txs are structured is that the first output of first tx is empty with 0 value
if (block.vtx[0].vout.size() != 1 || !block.vtx[0].vout[0].IsEmpty())
return state.DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));

// Second transaction must be coinstake, the rest must not be
// Ensure there is only one stake in block and it's 1st tx:
if (block.vtx.empty() || !block.vtx[1].IsCoinStake())
return state.DoS(100, error("CheckBlock() : second tx is not coinstake"));

// Ensure there are no other stakes in tx
for (unsigned int i = 2; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinStake())
return state.DoS(100, error("CheckBlock() : more than one coinstake"));

// If consensus level checks are in place.
// After spork is enabled we'll perform additional POS checks
// If the spork is not enabled these conditions are NOT checked
if (IsSporkActive(SPORK_23_STAKING_REQUIREMENTS) && block.GetBlockTime() >= GetSporkValue(SPORK_23_STAKING_REQUIREMENTS)) {
// Check for minimum value.
if (block.vtx[1].vout[1].nValue < Params().Stake_MinAmount())
return state.DoS(100, error("CheckBlock() : stake under min. stake value"));
CAmount minStakeAmount = Params().Stake_MinAmount();
unsigned int minStakeAge = nStakeMinAgeConsensus;
int minStakeConfirmations = Params().Stake_MinConfirmations();

// Check for coin age.
// First try finding the previous transaction in database.

// Get transaction of the staked input
CTransaction txPrev;
uint256 hashBlockPrev;
if (!GetTransaction(block.vtx[1].vin[0].prevout.hash, txPrev, hashBlockPrev, true))
return state.DoS(100, error("CheckBlock() : stake failed to find vin transaction"));
// Find block in map.

// Bulwark's "Re-Stake". Because Bulwark stores metadata identifying stake in tx we know that previously this was a POS reward
// If you have not previously staked on this input then you will have to wait longer for your stake to mature.
// This penalizes "Stake Grinding" and gives reason to leave stakes alone reducing traffic on the network.
if (IsSporkActive(SPORK_25_BWK_RESTAKE) && block.GetBlockTime() >= GetSporkValue(SPORK_25_BWK_RESTAKE)) {
// Time is doubled if it's not a basic steak (without split)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, stake spelling mistake

if (!txPrev.IsCoinStake() || txPrev.vout.size() != 3) {
minStakeAge *= 2;
minStakeConfirmations *= 2;
}
}
// Ensure the output of the stake is above min amount (100 for BWK)
if (block.vtx[1].vout[1].nValue < minStakeAmount)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't this achieve the same as the code deleted above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the amount didn't change but I wanted to group the logic together.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

return state.DoS(100, error("CheckBlock() : stake under min. stake value"));

// Get block of this transaction (the transaction of staked input)
CBlockIndex* pindex = NULL;
BlockMap::iterator it = mapBlockIndex.find(hashBlockPrev);
if (it != mapBlockIndex.end())
pindex = it->second;
else
return state.DoS(100, error("CheckBlock() : stake failed to find block index"));
// Check block time vs stake age requirement.
if (pindex->GetBlockHeader().nTime + nStakeMinAgeConsensus > GetAdjustedTime())

// Ensure the block age of the staked input block is at least nStakeMinAgeConsensus (12 * 60 * 60 for BWK)
//@todo this should probably look at chainActive.Tip()->GetBlockTime() not local computer GetAdjustedTime()
if (pindex->GetBlockHeader().nTime + minStakeAge > GetAdjustedTime())
return state.DoS(100, error("CheckBlock() : stake under min. stake age"));
// Check that the prev. stake block has required confirmations by height.
if (chainActive.Tip()->nHeight - pindex->nHeight < Params().Stake_MinConfirmations())

// Ensure that the difference between latest block and staked block meets min confirmations requirement (475 for BWK)
if (chainActive.Tip()->nHeight - pindex->nHeight < minStakeConfirmations)
return state.DoS(100, error("CheckBlock() : stake under min. required confirmations"));
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/spork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ int64_t GetSporkValue(int nSporkID) {
if (nSporkID == SPORK_21_ENABLE_ZEROCOIN) r = SPORK_21_ENABLE_ZEROCOIN_DEFAULT;
if (nSporkID == SPORK_22_ZEROCOIN_MAINTENANCE_MODE) r = SPORK_22_ZEROCOIN_MAINTENANCE_MODE_DEFAULT;
if (nSporkID == SPORK_23_STAKING_REQUIREMENTS) r = SPORK_23_STAKING_REQUIREMENTS_DEFAULT;
if (nSporkID == SPORK_24_QUERY_NODES) r = SPORK_24_QUERY_NODES_DEFAULT;
if (nSporkID == SPORK_25_BWK_RESTAKE) r = SPORK_25_BWK_RESTAKE_DEFAULT;

if (r == -1) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID);
}
Expand Down Expand Up @@ -286,6 +288,8 @@ int CSporkManager::GetSporkIDByName(std::string strName) {
if (strName == "SPORK_21_ENABLE_ZEROCOIN") return SPORK_21_ENABLE_ZEROCOIN;
if (strName == "SPORK_22_ZEROCOIN_MAINTENANCE_MODE") return SPORK_22_ZEROCOIN_MAINTENANCE_MODE;
if (strName == "SPORK_23_STAKING_REQUIREMENTS") return SPORK_23_STAKING_REQUIREMENTS;
if (strName == "SPORK_24_QUERY_NODES") return SPORK_24_QUERY_NODES;
if (strName == "SPORK_25_BWK_RESTAKE") return SPORK_25_BWK_RESTAKE;

return -1;
}
Expand All @@ -311,6 +315,8 @@ std::string CSporkManager::GetSporkNameByID(int id) {
if (id == SPORK_21_ENABLE_ZEROCOIN) return "SPORK_21_ENABLE_ZEROCOIN";
if (id == SPORK_22_ZEROCOIN_MAINTENANCE_MODE) return "SPORK_22_ZEROCOIN_MAINTENANCE_MODE";
if (id == SPORK_23_STAKING_REQUIREMENTS) return "SPORK_23_STAKING_REQUIREMENTS";
if (id == SPORK_24_QUERY_NODES) return "SPORK_24_QUERY_NODES";
if (id == SPORK_25_BWK_RESTAKE) return "SPORK_25_BWK_RESTAKE";

return "Unknown";
}
4 changes: 4 additions & 0 deletions src/spork.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ using namespace boost;
#define SPORK_21_ENABLE_ZEROCOIN 10020
#define SPORK_22_ZEROCOIN_MAINTENANCE_MODE 10021
#define SPORK_23_STAKING_REQUIREMENTS 10022
#define SPORK_24_QUERY_NODES 10023

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this spork used for?its not used anywhere in this pr afaik

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is for another upcoming feature Penple is looking into.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

#define SPORK_25_BWK_RESTAKE 10024

#define SPORK_2_SWIFTTX_DEFAULT 978307200 //2001-1-1
#define SPORK_3_SWIFTTX_BLOCK_FILTERING_DEFAULT 1424217600 //2015-2-18
Expand All @@ -73,6 +75,8 @@ using namespace boost;
#define SPORK_21_ENABLE_ZEROCOIN_DEFAULT 4070908800 //OFF
#define SPORK_22_ZEROCOIN_MAINTENANCE_MODE_DEFAULT 4070908800 //OFF
#define SPORK_23_STAKING_REQUIREMENTS_DEFAULT 4070908800 //OFF
#define SPORK_24_QUERY_NODES_DEFAULT 4070908800 //OFF
#define SPORK_25_BWK_RESTAKE_DEFAULT 4070908800 //OFF

class CSporkMessage;
class CSporkManager;
Expand Down
4 changes: 2 additions & 2 deletions src/test/main_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test) {
for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
/* @TODO fix subsidity, add nBits */
CAmount nSubsidy = GetBlockValue(nHeight);
BOOST_CHECK(nSubsidy <= 50 * COIN);
BOOST_CHECK_MESSAGE(nSubsidy <= 50 * COIN,"nSubsidy is " << nSubsidy << ". Expected 50 * COIN.");
nSum += nSubsidy * 1000;
BOOST_CHECK(MoneyRange(nSum));
BOOST_CHECK_MESSAGE(MoneyRange(nSum),"nSum is " << nSum << ". MaxMoneyOut: " << Params().MaxMoneyOut());
}
BOOST_CHECK(nSum == 2099999997690000ULL);
}
Expand Down
16 changes: 14 additions & 2 deletions src/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,9 @@ bool CWallet::SelectStakeCoins(std::set<std::pair<const CWalletTx*, unsigned int
}

BOOST_FOREACH(const COutput& out, vCoins) {
unsigned int minStakeAge = nMinStakeAge;
int minStakeDepth = nStakeDepth;

//make sure not to outrun target amount
if (nAmountSelected + out.tx->vout[out.i].nValue > nTargetAmount)
continue;
Expand All @@ -1704,8 +1707,17 @@ bool CWallet::SelectStakeCoins(std::set<std::pair<const CWalletTx*, unsigned int
if (out.tx->vout[out.i].nValue < nStakeAmount)
continue;

//For Bulwark Re-staking since we know if a tx is previous stake it'll be considered re-stake resulting in various advantages

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this adds more incentive to stake grind, but in a different way,might wanna see how this plays out on a testnet

if (IsSporkActive(SPORK_25_BWK_RESTAKE)) {
// Basic restakes will receive rewards 2x faster. (These must not be split)
if (!out.tx->IsCoinStake() || out.tx->vout.size()!=3) {
minStakeAge *= 2;
minStakeDepth *= 2;
}
}

//check that it is matured
if (out.nDepth < (out.tx->IsCoinStake() ? nStakeDepth : 10))
if (out.nDepth < (out.tx->IsCoinStake() ? minStakeDepth : 10))
continue;

//if zerocoinspend, then use the block time
Expand All @@ -1717,7 +1729,7 @@ bool CWallet::SelectStakeCoins(std::set<std::pair<const CWalletTx*, unsigned int
}

//check for min age
if (GetAdjustedTime() - nTxTime < nMinStakeAge)
if (GetAdjustedTime() - nTxTime < minStakeAge)
continue;

//add to our stake set
Expand Down