Skip to content

Commit

Permalink
staking: use 320-bit integers for target calculations
Browse files Browse the repository at this point in the history
While a non-weighted stake target hash can fit 236-bit integers[0], the
multiplication by the 64-bit stake can cause the weighted target hash
to go up to 300-bits. While I think this would have been better fixed
by lowering the stake target hash limit to be 192-bit so that the
weighted target hash fits 256-bit, this would break consensus and there
are already blocks that require weighted target hashes higher than 2**256
to verify.

The existing blob_int class requires bits which are multiples of 32, so
320 is used.

[0] - src/gridcoin/staking/difficulty.cpp:24 # The PROOF_OF_STAKE_LIMIT
is set to uint256 maximum right shifted by 20 resulting in a 236-bit
limit. That's enforced by the GRC::GetNextTargetRequired function which
is used to generate the nBits of blocks.
  • Loading branch information
div72 committed Oct 19, 2023
1 parent f7c13be commit 53400d5
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 9 deletions.
21 changes: 20 additions & 1 deletion src/arith_uint256.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <uint256.h>
#include <crypto/common.h>
#include <util/strencodings.h>


template <unsigned int BITS>
Expand Down Expand Up @@ -146,7 +147,13 @@ double base_uint<BITS>::getdouble() const
template <unsigned int BITS>
std::string base_uint<BITS>::GetHex() const
{
return ArithToUint256(*this).GetHex();
static constexpr ssize_t BYTES = BITS / 8;

uint8_t pn_rev[BYTES];
for (int i = 0; i < BYTES; ++i) {
pn_rev[i] = ((uint8_t*)&pn)[BYTES - 1 - i];
}
return HexStr(pn_rev);
}

template <unsigned int BITS>
Expand Down Expand Up @@ -257,3 +264,15 @@ arith_uint256 UintToArith256(const uint256 &a)
b.pn[x] = ReadLE32(a.begin() + x*4);
return b;
}

// Explicit instantiations for base_uint<320>
template base_uint<320>& base_uint<320>::operator<<=(unsigned int);
template base_uint<320>& base_uint<320>::operator*=(const base_uint<320>& b);
template int base_uint<320>::CompareTo(const base_uint<320>&) const;
template std::string base_uint<320>::GetHex() const;

arith_uint320::arith_uint320(const uint256& b) {
std::memset(pn, 0, sizeof(pn));
std::memcpy(pn, b.data(), b.size());
}

17 changes: 17 additions & 0 deletions src/arith_uint256.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>

class uint256;
class arith_uint320;

class uint_error : public std::runtime_error {
public:
Expand Down Expand Up @@ -279,6 +280,22 @@ class arith_uint256 : public base_uint<256> {

friend uint256 ArithToUint256(const arith_uint256 &);
friend arith_uint256 UintToArith256(const uint256 &);
friend class arith_uint320;
};

/** 320-bit unsigned big integer. */
class arith_uint320 : public base_uint<320> {
public:
arith_uint320() {}
arith_uint320(const base_uint<320>& b) : base_uint<320>(b) {}
arith_uint320(uint64_t b) : base_uint<320>(b) {}

arith_uint320(const arith_uint256& b) {
std::memset(pn, 0, sizeof(pn));
std::memcpy(pn, b.pn, sizeof(b.pn));
}

arith_uint320(const uint256& b);
};

uint256 ArithToUint256(const arith_uint256 &);
Expand Down
7 changes: 3 additions & 4 deletions src/gridcoin/staking/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,13 +631,12 @@ bool GRC::CheckProofOfStakeV8(

//Stake refactoring TomasBrod
int64_t Weight = CalculateStakeWeightV8(txPrev, prevout.n);
arith_uint256 bnHashProof = UintToArith256(hashProofOfStake);
arith_uint320 bnHashProof = arith_uint320(hashProofOfStake);

// Base target
arith_uint256 bnTarget;
bnTarget.SetCompact(Block.nBits);
arith_uint320 bnTarget = arith_uint256().SetCompact(Block.nBits);
// Weighted target
bnTarget *= Weight;
bnTarget *= arith_uint320(Weight);


LogPrint(BCLog::LogFlags::VERBOSE,
Expand Down
7 changes: 3 additions & 4 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,8 @@ bool CreateCoinStake(CBlock &blocknew, CKey &key,

StakeKernelHash = UintToArith256(GRC::CalculateStakeHashV8(block_time, CoinTx, CoinTxN, txnew.nTime, StakeModifier));

arith_uint256 StakeTarget;
StakeTarget.SetCompact(blocknew.nBits);
StakeTarget *= CoinWeight;
arith_uint320 StakeTarget = arith_uint256().SetCompact(blocknew.nBits);
StakeTarget *= arith_uint320(CoinWeight);
StakeWeightSum += CoinWeight;
StakeWeightMin = std::min(StakeWeightMin, CoinWeight);
StakeWeightMax = std::max(StakeWeightMax, CoinWeight);
Expand All @@ -754,7 +753,7 @@ bool CreateCoinStake(CBlock &blocknew, CKey &key,
StakeKernelDiff,
GRC::GetBlockDifficulty(blocknew.nBits));

if (StakeKernelHash <= StakeTarget)
if (arith_uint320(StakeKernelHash) <= StakeTarget)
{
// Found a kernel
LogPrintf("CreateCoinStake: Found Kernel");
Expand Down

0 comments on commit 53400d5

Please sign in to comment.