Skip to content

Commit

Permalink
consensus: update main consensus/hashing rules
Browse files Browse the repository at this point in the history
  • Loading branch information
barrystyle committed Oct 28, 2023
1 parent 5b4f4d1 commit b6c50bc
Show file tree
Hide file tree
Showing 20 changed files with 199 additions and 339 deletions.
2 changes: 1 addition & 1 deletion src/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex*
// peercoin: find last block index up to pindex
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
{
while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake))
while (pindex && pindex->pprev && ((pindex->nNonce == 0) != fProofOfStake))
pindex = pindex->pprev;
return pindex;
}
11 changes: 3 additions & 8 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,22 +236,17 @@ class CBlockIndex

bool IsProofOfWork() const
{
return !(nFlags & BLOCK_PROOF_OF_STAKE);
return !IsProofOfStake();
}

bool IsProofOfStake() const
{
return (nFlags & BLOCK_PROOF_OF_STAKE);
}

void SetProofOfStake()
{
nFlags |= BLOCK_PROOF_OF_STAKE;
return nNonce == 0;
}

unsigned int GetStakeEntropyBit() const
{
return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
return ((GetBlockHash().GetLow64()) & 1llu);
}

bool SetStakeEntropyBit(unsigned int nEntropyBit)
Expand Down
26 changes: 13 additions & 13 deletions src/chainparamsseeds.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
* Each line contains a BIP155 serialized (networkID, addr, port) tuple.
*/
static const uint8_t chainparams_seed_main[] = {
0x01,0x04,0x87,0xb5,0xa4,0xbd,0x26,0xad,
0x01,0x04,0x57,0x62,0xb9,0xf4,0x26,0xad,
0x01,0x04,0x8b,0x3b,0x81,0xb3,0x26,0xad,
0x01,0x04,0x2f,0xce,0x58,0x12,0x26,0xad,
0x01,0x04,0x05,0x80,0x57,0x7e,0x26,0xad,
0x01,0x04,0xd5,0x9f,0x51,0xe5,0x26,0xad,
0x01,0x04,0x2e,0x65,0x4b,0x98,0x26,0xad,
0x01,0x04,0xb2,0x72,0xe9,0x28,0x26,0xad,
0x01,0x04,0x80,0xc7,0x28,0x23,0x26,0xad,
0x01,0x04,0x44,0xb7,0xcf,0xaf,0x26,0xad,
0x01,0x04,0xbc,0xa6,0x0c,0x61,0x26,0xad,
0x01,0x04,0x9f,0x45,0x25,0x11,0x26,0xad,
0x01,0x04,0x4a,0x2c,0xcf,0xa3,0x26,0xad,
0x01,0x04,0xad,0x45,0x86,0xfa,0x42,0x76,
0x01,0x04,0xb8,0x67,0x55,0xe0,0x42,0x76,
0x01,0x04,0xbd,0xa5,0xe1,0x8c,0x42,0x76,
0x01,0x04,0x18,0x87,0x04,0xca,0x42,0x76,
0x01,0x04,0x41,0x6d,0x1f,0xbb,0x42,0x76,
0x01,0x04,0x5f,0xd9,0x4d,0xd1,0x42,0x76,
0x01,0x04,0x89,0xb8,0x2d,0x9b,0x42,0x76,
0x01,0x04,0x8f,0xf4,0xd1,0xa2,0x42,0x76,
0x01,0x04,0x05,0xb4,0x19,0x56,0x42,0x76,
0x01,0x04,0x05,0xb4,0x19,0x57,0x42,0x76,
0x01,0x04,0x42,0x81,0x9c,0xb3,0x42,0x76,
0x01,0x04,0x52,0x12,0xd1,0xf1,0x42,0x76,
0x01,0x04,0x55,0xa6,0x1c,0x37,0x42,0x76,
};

static const uint8_t chainparams_seed_test[] = {
Expand Down
4 changes: 2 additions & 2 deletions src/consensus/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;

static constexpr CAmount COIN = 1000000;
static constexpr CAmount CENT = 10000;
static constexpr CAmount COIN = 100000000;
static constexpr CAmount CENT = 1000000;

static const CAmount MIN_TX_FEE_PREV7 = CENT;
static const CAmount MIN_TX_FEE = CENT / 10;
Expand Down
7 changes: 4 additions & 3 deletions src/consensus/tx_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");

// peercoin: enforce minimum output amount
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT &&
(tx.nVersion < 3 && !(IsZeroAllowed(tx.nTime) && (txout.nValue == 0))))
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txoutvalue-belowminimum");
//if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT &&
// (tx.nVersion < 3 && !(IsZeroAllowed(tx.nTime) && (txout.nValue == 0))))
// return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txoutvalue-belowminimum");
}

// Check for duplicate inputs (see CVE-2018-17144)
Expand Down
195 changes: 26 additions & 169 deletions src/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <bignum.h>
#include <txdb.h>
#include <consensus/validation.h>
#include <pow.h>
#include <util/system.h>
#include <validation.h>
#include <random.h>
Expand Down Expand Up @@ -50,40 +51,6 @@ const unsigned int nProtocolV10TestSwitchTime = 1625140800; // Thu 1 Jul 12:00:
const unsigned int nProtocolV12SwitchTime = 1681732800; // Mon 17 Apr 12:00:00 UTC 2023
const unsigned int nProtocolV12TestSwitchTime = 1669636800; // Mon 28 Nov 12:00:00 UTC 2022

// Hard checkpoints of stake modifiers to ensure they are deterministic
static std::map<int, unsigned int> mapStakeModifierCheckpoints =
boost::assign::map_list_of
( 0, 0x0e00670bu )
( 19080, 0xad4e4d29u )
( 30583, 0xdc7bf136u )
( 99999, 0xf555cfd2u )
(219999, 0x91b7444du )
(336000, 0x6c3c8048u )
(371850, 0x9b850bdfu )
(407813, 0x46fe50b5u )
(443561, 0x114a6e38u )
(455470, 0x9b7af181u )
(479189, 0xe04fb8e0u )
(504051, 0x459f5a16u )
(589659, 0xbd02492au )
;

static std::map<int, unsigned int> mapStakeModifierTestnetCheckpoints =
boost::assign::map_list_of
( 0, 0x0e00670bu )
( 19080, 0x3711dc3au )
( 30583, 0xb480fadeu )
( 99999, 0x9a62eaecu )
(219999, 0xeafe96c3u )
(336000, 0x8330dc09u )
(372751, 0xafb94e2fu )
(382019, 0x7f5cf5ebu )
(408500, 0x68cadee2u )
(412691, 0x93138e67u )
(441299, 0x03e195cbu )
(442735, 0xe42d94feu )
;

// Whether the given coinstake is subject to new v0.3 protocol
bool IsProtocolV03(unsigned int nTimeCoinStake)
{
Expand Down Expand Up @@ -218,9 +185,8 @@ static bool SelectBlockFromCandidates(
continue;
// compute the selection hash by hashing its proof-hash and the
// previous proof-of-stake modifier
uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
CDataStream ss(SER_GETHASH, 0);
ss << hashProof << nStakeModifierPrev;
ss << pindex->hashProofOfStake << nStakeModifierPrev;
arith_uint256 hashSelection = UintToArith256(Hash(ss));
// 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
Expand Down Expand Up @@ -281,25 +247,10 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t &nStake
LogPrintf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime());
return true;
}
if (nModifierTime / params.nModifierInterval >= pindexCurrent->GetBlockTime() / params.nModifierInterval)
{
// v0.4+ requires current block timestamp also be in a different modifier interval
if (IsProtocolV04(pindexCurrent->nTime))
{
if (gArgs.GetBoolArg("-debug", false))
LogPrintf("ComputeNextStakeModifier: (v0.4+) no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
return true;
}
else
{
if (gArgs.GetBoolArg("-debug", false))
LogPrintf("ComputeNextStakeModifier: v0.3 modifier at block %s not meeting v0.4+ protocol: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
}
}

// Sort candidate blocks by timestamp
vector<pair<int64_t, uint256> > vSortedByTimestamp;
vSortedByTimestamp.reserve(64 * params.nModifierInterval / params.nStakeTargetSpacing);
vSortedByTimestamp.reserve(64 * params.nModifierInterval / GetTargetSpacing(pindexPrev->nHeight));
int64_t nSelectionInterval = GetStakeModifierSelectionInterval();
int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / params.nModifierInterval) * params.nModifierInterval - nSelectionInterval;
const CBlockIndex* pindex = pindexPrev;
Expand Down Expand Up @@ -383,47 +334,6 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t &nStake
return true;
}

// V0.5: Stake modifier used to hash for a stake kernel is chosen as the stake
// modifier that is (nStakeMinAge minus a selection interval) earlier than the
// stake, thus at least a selection interval later than the coin generating the
// kernel, as the generating coin is from at least nStakeMinAge ago.
static bool GetKernelStakeModifierV05(CBlockIndex* pindexPrev, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
{
const Consensus::Params& params = Params().GetConsensus();
const CBlockIndex* pindex = pindexPrev;
nStakeModifierHeight = pindex->nHeight;
nStakeModifierTime = pindex->GetBlockTime();
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();

if (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval <= (int64_t) nTimeTx)
{
// Best block is still more than
// (nStakeMinAge minus a selection interval) older than kernel timestamp
if (fPrintProofOfStake)
return error("GetKernelStakeModifier() : best block %s at height %d too old for stake",
pindex->GetBlockHash().ToString(), pindex->nHeight);
else
return false;
}
// loop to find the stake modifier earlier by
// (nStakeMinAge minus a selection interval)
while (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval >(int64_t) nTimeTx)
{
if (!pindex->pprev)
{ // reached genesis block; should not happen
return error("GetKernelStakeModifier() : reached genesis block");
}
pindex = pindex->pprev;
if (pindex->GeneratedStakeModifier())
{
nStakeModifierHeight = pindex->nHeight;
nStakeModifierTime = pindex->GetBlockTime();
}
}
nStakeModifier = pindex->nStakeModifier;
return true;
}

// V0.3: 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
static bool GetKernelStakeModifierV03(CBlockIndex* pindexPrev, uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, Chainstate& chainstate)
Expand Down Expand Up @@ -483,10 +393,7 @@ static bool GetKernelStakeModifierV03(CBlockIndex* pindexPrev, uint256 hashBlock
// Get the stake modifier specified by the protocol to hash for a stake kernel
static bool GetKernelStakeModifier(CBlockIndex* pindexPrev, uint256 hashBlockFrom, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, Chainstate& chainstate)
{
if (IsProtocolV05(nTimeTx))
return GetKernelStakeModifierV05(pindexPrev, nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake);
else
return GetKernelStakeModifierV03(pindexPrev, hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate);
return GetKernelStakeModifierV03(pindexPrev, hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate);
}

// peercoin kernel protocol
Expand Down Expand Up @@ -520,68 +427,44 @@ bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBl
const Consensus::Params& params = Params().GetConsensus();
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();

if (nTimeTx < (txPrev->nTime? txPrev->nTime : nTimeBlockFrom)) // Transaction timestamp violation
if (nTimeTx < txPrev->nTime) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");

if (nTimeBlockFrom + params.nStakeMinAge > nTimeTx) // Min age requirement
return error("CheckStakeKernelHash() : min age violation");

CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
CBigNum bnTarget;
bnTarget.SetCompact(nBits);

int64_t 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_t nTimeWeight = min((int64_t)nTimeTx - (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), params.nStakeMaxAge) - (IsProtocolV03(nTimeTx)? params.nStakeMinAge : 0);
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
CBigNum bnWeight = CBigNum(nValueIn);
bnTarget *= bnWeight;

uint256 targetProofOfStake = bnTarget.getuint256();

uint64_t nStakeModifier = pindexPrev->nStakeModifier;
int nStakeModifierHeight = pindexPrev->nHeight;
int64_t nStakeModifierTime = pindexPrev->nTime;

// Calculate hash
CDataStream ss(SER_GETHASH, 0);
uint64_t nStakeModifier = 0;
int nStakeModifierHeight = 0;
int64_t nStakeModifierTime = 0;
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
{
if (!GetKernelStakeModifier(pindexPrev, blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate))
return false;
ss << nStakeModifier;
}
else // v0.2 protocol
{
ss << nBits;
}

ss << nTimeBlockFrom << nTxPrevOffset << (txPrev->nTime? txPrev->nTime : nTimeBlockFrom) << prevout.n << nTimeTx;
ss << nStakeModifier << nTimeBlockFrom << nTxPrevOffset << txPrev->nTime << prevout.n << nTimeTx;
hashProofOfStake = Hash(ss);

if (fPrintProofOfStake)
{
if (IsProtocolV03(nTimeTx)) {
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
FormatISO8601DateTime(nStakeModifierTime),
pindexTmp->nHeight,
FormatISO8601DateTime(blockFrom.GetBlockTime()));
}
LogPrintf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016x 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_t) nBits,
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
nStakeModifier,
nTimeBlockFrom, nTxPrevOffset, (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), prevout.n, nTimeTx,
hashProofOfStake.ToString());
}

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

if (gArgs.GetBoolArg("-debug", false) && !fPrintProofOfStake)
{
if (IsProtocolV03(nTimeTx)) {
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
nStakeModifier, nStakeModifierHeight,
FormatISO8601DateTime(nStakeModifierTime),
pindexTmp->nHeight,
FormatISO8601DateTime(blockFrom.GetBlockTime()));
}
LogPrintf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
IsProtocolV03(nTimeTx)? "0.3" : "0.2",
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
Expand Down Expand Up @@ -670,19 +553,6 @@ unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
return hashChecksum.GetLow64();
}

// Check stake modifier hard checkpoints
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
{
bool fTestNet = Params().NetworkIDString() == CBaseChainParams::TESTNET;
if (fTestNet && mapStakeModifierTestnetCheckpoints.count(nHeight))
return nStakeModifierChecksum == mapStakeModifierTestnetCheckpoints[nHeight];

if (!fTestNet && mapStakeModifierCheckpoints.count(nHeight))
return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];

return true;
}

bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
{
return (HowSuperMajority(minVersion, pstart, nRequired, nToCheck) >= nRequired);
Expand All @@ -708,22 +578,9 @@ unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigne
unsigned int GetStakeEntropyBit(const CBlock& block)
{
unsigned int nEntropyBit = 0;
if (IsProtocolV04(block.nTime))
{
nEntropyBit = UintToArith256(block.GetHash()).GetLow64() & 1llu;// last bit of block hash
if (gArgs.GetBoolArg("-printstakemodifier", false))
LogPrintf("GetStakeEntropyBit(v0.4+): nTime=%u hashBlock=%s entropybit=%d\n", block.nTime, block.GetHash().ToString(), nEntropyBit);
}
else
{
// old protocol for entropy bit pre v0.4
uint160 hashSig = Hash160(block.vchBlockSig);
if (gArgs.GetBoolArg("-printstakemodifier", false))
LogPrintf("GetStakeEntropyBit(v0.3): nTime=%u hashSig=%s", block.nTime, hashSig.ToString());
nEntropyBit = hashSig.data()[19] >> 7; // take the first bit of the hash
if (gArgs.GetBoolArg("-printstakemodifier", false))
LogPrintf(" entropybit=%d\n", nEntropyBit);
}
nEntropyBit = UintToArith256(block.GetHash()).GetLow64() & 1llu;// last bit of block hash
if (gArgs.GetBoolArg("-printstakemodifier", false))
LogPrintf("GetStakeEntropyBit(v0.4+): nTime=%u hashBlock=%s entropybit=%d\n", block.nTime, block.GetHash().ToString(), nEntropyBit);
return nEntropyBit;
}

4 changes: 0 additions & 4 deletions src/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,10 @@ bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx);
// Get stake modifier checksum
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex);

// Check stake modifier hard checkpoints
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum);

// peercoin: block version supermajority calculation
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);

// peercoin: entropy bit for stake modifier if chosen by modifier
unsigned int GetStakeEntropyBit(const CBlock& block);

#endif // PEERCOIN_KERNEL_H
Loading

0 comments on commit b6c50bc

Please sign in to comment.