From 8c05aa388b7dc53928be7cc8e79a52baf30889bc Mon Sep 17 00:00:00 2001 From: div72 Date: Thu, 19 Oct 2023 22:45:14 +0300 Subject: [PATCH] staking: use 320-bit integers for target calculations 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. --- src/arith_uint256.cpp | 21 ++++++++++++++++++++- src/arith_uint256.h | 17 +++++++++++++++++ src/gridcoin/staking/kernel.cpp | 7 +++---- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index 536a787fee..946c47a384 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -7,6 +7,7 @@ #include #include +#include template @@ -146,7 +147,13 @@ double base_uint::getdouble() const template std::string base_uint::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 @@ -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()); +} + diff --git a/src/arith_uint256.h b/src/arith_uint256.h index c63a76ea03..26d6a45ca7 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -13,6 +13,7 @@ #include class uint256; +class arith_uint320; class uint_error : public std::runtime_error { public: @@ -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 &); diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index 511ef3fe18..7f1952140d 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -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,